Skip to content

Commit 4fbd732

Browse files
committed
Added methods to easily add a unit to the interface or implementation uses section.
1 parent be278b1 commit 4fbd732

10 files changed

+1486
-2
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,5 @@ __recovery/
6868
# Boss dependency manager vendor folder https://github.com/HashLoad/boss
6969
modules/
7070
*.res
71+
*.cbk
72+
dunitx-results.xml

GDK.ToolsAPI.Helper.Interfaces.pas

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ EToolsApiNoCustomLogGroupUsed = class(Exception);
107107
function Reader: IToolsApiEditReader;
108108
function Writer: IToolsApiEditWriter;
109109
function UndoableWriter: IToolsApiEditWriter;
110+
111+
procedure AddToInterfaceUses(const UnitName: string);
112+
procedure AddToImplementationUses(const UnitName: string);
110113
end;
111114

112115
IToolsApiEditReader = interface

GDK.ToolsAPI.Helper.pas

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,17 @@ TToolsApiEditView = class(TInterfacedObject, IToolsApiEditView)
104104
TToolsApiSourceEditor = class(TInterfacedObject, IToolsApiSourceEditor)
105105
private
106106
FEditor: IOTASourceEditor;
107+
procedure AddToUses(const UnitName: string; const AddToImplementation: Boolean);
107108
public
108109
constructor Create(const Editor: IOTASourceEditor);
109110

110111
function Get: IOTASourceEditor;
111112
function Reader: IToolsApiEditReader;
112113
function Writer: IToolsApiEditWriter;
113114
function UndoableWriter: IToolsApiEditWriter;
115+
116+
procedure AddToInterfaceUses(const UnitName: string);
117+
procedure AddToImplementationUses(const UnitName: string);
114118
end;
115119

116120
TToolsApiEditReader = class(TInterfacedObject, IToolsApiEditReader)
@@ -163,7 +167,8 @@ implementation
163167
uses
164168
System.Classes,
165169
DCCStrs,
166-
GDK.ToolsAPI.ProjectManagerContextMenu;
170+
GDK.ToolsAPI.ProjectManagerContextMenu,
171+
GDK.ToolsAPI.UsesManager;
167172

168173
{ TToolsApiHelper }
169174

@@ -425,6 +430,38 @@ function TToolsApiSourceEditor.UndoableWriter: IToolsApiEditWriter;
425430
Result := TToolsApiEditWriter.Create(Writer);
426431
end;
427432

433+
procedure TToolsApiSourceEditor.AddToInterfaceUses(const UnitName: string);
434+
begin
435+
AddToUses(UnitName, False);
436+
end;
437+
438+
procedure TToolsApiSourceEditor.AddToImplementationUses(const UnitName: string);
439+
begin
440+
AddToUses(UnitName, True);
441+
end;
442+
443+
procedure TToolsApiSourceEditor.AddToUses(const UnitName: string; const AddToImplementation: Boolean);
444+
begin
445+
var EditorContent := Self.Reader.Content;
446+
447+
TToolsApiUsesManager
448+
.Use
449+
.WithSource(EditorContent)
450+
.FindPositionToAdd(UnitName, AddToImplementation,
451+
procedure(const Position: Integer; const IsEmptyUses: Boolean)
452+
begin
453+
var UsesText := '';
454+
if IsEmptyUses then
455+
UsesText := sLineBreak +
456+
sLineBreak + 'uses' +
457+
sLineBreak + ' ' + UnitName + ';'
458+
else
459+
UsesText := ', ' + UnitName;
460+
461+
Self.Writer.InsertText(UsesText, Position);
462+
end);
463+
end;
464+
428465
{ TToolsApiEditReader }
429466

430467
constructor TToolsApiEditReader.Create(const Reader: IOTAEditReader);

GDK.ToolsAPI.UsesManager.pas

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
unit GDK.ToolsAPI.UsesManager;
2+
3+
interface
4+
5+
uses
6+
System.RegularExpressions,
7+
System.SysUtils;
8+
9+
type
10+
TUsesResult = record
11+
InterfaceSection: TMatch;
12+
ImplementationSection: TMatch;
13+
end;
14+
15+
TOnPositionFoundProc = reference to procedure(const PositionToAdd: Integer; const IsEmptyUses: Boolean);
16+
17+
IToolsApiUsesManager = interface
18+
['{E5AACAFF-3D39-4F9D-BEAC-4DB6577ADAF8}']
19+
20+
function WithSource(const Source: string): IToolsApiUsesManager;
21+
22+
function FindUses: TUsesResult;
23+
function FindWord(const Word: string): TMatch;
24+
25+
procedure FindPositionToAdd(const UnitName: string; const InImplementation: Boolean; const OnPositionFound: TOnPositionFoundProc);
26+
end;
27+
28+
TToolsApiUsesManager = class(TInterfacedObject, IToolsApiUsesManager)
29+
const
30+
REGEX_INTERFACE = '\binterface\b[\h\s\w[.,]';
31+
REGEX_IMPLEMENTATION = '\bimplementation\b[\h\s\w[.,]';
32+
REGEX_USES_SECTION = '\buses\b[\h\s\w[.,]*;';
33+
private
34+
FSource: string;
35+
36+
function GetPositionForInterface: Integer;
37+
function GetPositionForImplementation: Integer;
38+
function GetEndOfSectionKeyword(const ForImplementation: Boolean): Integer;
39+
40+
function DoFindWord(const Source: string; const Word: string): TMatch;
41+
function FindPositionToAdd(const UnitName: string; const UsesMatch: TMatch): Integer; overload;
42+
public
43+
function WithSource(const Source: string): IToolsApiUsesManager;
44+
45+
function FindUses: TUsesResult;
46+
function FindWord(const Word: string): TMatch;
47+
48+
procedure FindPositionToAdd(const UnitName: string; const InImplementation: Boolean; const OnPositionFound: TOnPositionFoundProc); overload;
49+
50+
class function Use: IToolsApiUsesManager;
51+
end;
52+
53+
EUnitAlreadyInImplementation = class(Exception)
54+
private
55+
FPosition: Integer;
56+
public
57+
property Position: Integer read FPosition write FPosition;
58+
end;
59+
60+
implementation
61+
62+
{ TToolsApiUsesManager }
63+
64+
class function TToolsApiUsesManager.Use: IToolsApiUsesManager;
65+
begin
66+
Result := TToolsApiUsesManager.Create;
67+
end;
68+
69+
function TToolsApiUsesManager.WithSource(const Source: string): IToolsApiUsesManager;
70+
begin
71+
FSource := Source;
72+
Result := Self;
73+
end;
74+
75+
function TToolsApiUsesManager.FindUses: TUsesResult;
76+
begin
77+
var ImplementationPos := GetPositionForImplementation;
78+
var Matches := TRegEx.Matches(FSource, REGEX_USES_SECTION, [roIgnoreCase, roMultiLine]);
79+
80+
for var i := 0 to Matches.Count - 1 do
81+
begin
82+
var Match := Matches[i];
83+
if Match.Success then
84+
begin
85+
if (Match.Index < ImplementationPos) then
86+
Result.InterfaceSection := Match
87+
else
88+
Result.ImplementationSection := Match;
89+
end;
90+
end;
91+
end;
92+
93+
function TToolsApiUsesManager.FindWord(const Word: string): TMatch;
94+
begin
95+
Result := DoFindWord(FSource, Word);
96+
end;
97+
98+
function TToolsApiUsesManager.DoFindWord(const Source, Word: string): TMatch;
99+
begin
100+
var RegExPattern := '\b' + Word.Replace('.', '\.') + '\b';
101+
102+
Result := TRegEx.Match(Source, RegExPattern, [roIgnoreCase, roMultiLine]);
103+
end;
104+
105+
function TToolsApiUsesManager.GetPositionForInterface: Integer;
106+
begin
107+
var Match := TRegEx.Match(FSource, REGEX_INTERFACE, [roIgnoreCase, roMultiLine]);
108+
Result := Match.Index;
109+
end;
110+
111+
function TToolsApiUsesManager.GetPositionForImplementation: Integer;
112+
begin
113+
var Match := TRegEx.Match(FSource, REGEX_IMPLEMENTATION, [roIgnoreCase, roMultiLine]);
114+
Result := Match.Index;
115+
end;
116+
117+
function TToolsApiUsesManager.GetEndOfSectionKeyword(const ForImplementation: Boolean): Integer;
118+
begin
119+
if ForImplementation then
120+
Result := GetPositionForImplementation + 'implementation'.Length
121+
else
122+
Result := GetPositionForInterface + 'interface'.Length;
123+
end;
124+
125+
procedure TToolsApiUsesManager.FindPositionToAdd(const UnitName: string; const InImplementation: Boolean; const OnPositionFound: TOnPositionFoundProc);
126+
begin
127+
var UsesResult := FindUses;
128+
129+
var MatchInInterface := DoFindWord(UsesResult.InterfaceSection.Value, UnitName);
130+
if MatchInInterface.Success then
131+
Exit;
132+
133+
var MatchInImplementation := DoFindWord(UsesResult.ImplementationSection.Value, UnitName);
134+
if MatchInImplementation.Success then
135+
begin
136+
if InImplementation then
137+
Exit;
138+
139+
var MatchException := EUnitAlreadyInImplementation.CreateFmt('Unit "%s" already in implementation uses', [UnitName]);
140+
MatchException.Position := MatchInImplementation.Index;
141+
142+
raise MatchException;
143+
end;
144+
145+
var UsesMatch: TMatch;
146+
147+
if InImplementation then
148+
UsesMatch := UsesResult.ImplementationSection
149+
else
150+
UsesMatch := UsesResult.InterfaceSection;
151+
152+
var HasUsesSection := UsesMatch.Success;
153+
var Position: Integer;
154+
155+
if not HasUsesSection then
156+
begin
157+
Position := GetEndOfSectionKeyword(InImplementation);
158+
end
159+
else
160+
begin
161+
Position := FindPositionToAdd(UnitName, UsesMatch);
162+
end;
163+
164+
OnPositionFound(Position, not HasUsesSection);
165+
end;
166+
167+
function TToolsApiUsesManager.FindPositionToAdd(const UnitName: string; const UsesMatch: TMatch): Integer;
168+
begin
169+
Result := UsesMatch.Index + UsesMatch.Length - 2;
170+
end;
171+
172+
end.

GdkToolsApiGroupProject.groupproj

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2+
<PropertyGroup>
3+
<ProjectGuid>{02D94AB9-78A4-41B8-BA19-29E7F600BCB4}</ProjectGuid>
4+
</PropertyGroup>
5+
<ItemGroup>
6+
<Projects Include="GdkToolsApiHelper.dproj">
7+
<Dependencies/>
8+
</Projects>
9+
<Projects Include="UnitTesting\GdkToolsApiUnitTests.dproj">
10+
<Dependencies/>
11+
</Projects>
12+
</ItemGroup>
13+
<ProjectExtensions>
14+
<Borland.Personality>Default.Personality.12</Borland.Personality>
15+
<Borland.ProjectType/>
16+
<BorlandProject>
17+
<Default.Personality/>
18+
</BorlandProject>
19+
</ProjectExtensions>
20+
<Target Name="GdkToolsApiHelper">
21+
<MSBuild Projects="GdkToolsApiHelper.dproj"/>
22+
</Target>
23+
<Target Name="GdkToolsApiHelper:Clean">
24+
<MSBuild Projects="GdkToolsApiHelper.dproj" Targets="Clean"/>
25+
</Target>
26+
<Target Name="GdkToolsApiHelper:Make">
27+
<MSBuild Projects="GdkToolsApiHelper.dproj" Targets="Make"/>
28+
</Target>
29+
<Target Name="GdkToolsApiUnitTests">
30+
<MSBuild Projects="UnitTesting\GdkToolsApiUnitTests.dproj"/>
31+
</Target>
32+
<Target Name="GdkToolsApiUnitTests:Clean">
33+
<MSBuild Projects="UnitTesting\GdkToolsApiUnitTests.dproj" Targets="Clean"/>
34+
</Target>
35+
<Target Name="GdkToolsApiUnitTests:Make">
36+
<MSBuild Projects="UnitTesting\GdkToolsApiUnitTests.dproj" Targets="Make"/>
37+
</Target>
38+
<Target Name="Build">
39+
<CallTarget Targets="GdkToolsApiHelper;GdkToolsApiUnitTests"/>
40+
</Target>
41+
<Target Name="Clean">
42+
<CallTarget Targets="GdkToolsApiHelper:Clean;GdkToolsApiUnitTests:Clean"/>
43+
</Target>
44+
<Target Name="Make">
45+
<CallTarget Targets="GdkToolsApiHelper:Make;GdkToolsApiUnitTests:Make"/>
46+
</Target>
47+
<Import Project="$(BDS)\Bin\CodeGear.Group.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Group.Targets')"/>
48+
</Project>

GdkToolsApiHelper.dpk

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ contains
4040
GDK.ToolsApi.Notifiers.IDE in 'GDK.ToolsApi.Notifiers.IDE.pas',
4141
GDK.ToolsApi.Notifiers.ProjectStorage in 'GDK.ToolsApi.Notifiers.ProjectStorage.pas',
4242
GDK.ToolsAPI.CustomMessage in 'GDK.ToolsAPI.CustomMessage.pas',
43-
GDK.ToolsAPI.ProjectManagerContextMenu in 'GDK.ToolsAPI.ProjectManagerContextMenu.pas';
43+
GDK.ToolsAPI.ProjectManagerContextMenu in 'GDK.ToolsAPI.ProjectManagerContextMenu.pas',
44+
GDK.ToolsAPI.UsesManager in 'GDK.ToolsAPI.UsesManager.pas';
4445

4546
end.

GdkToolsApiHelper.dproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@
190190
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k280.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
191191
<Excluded_Packages Name="$(BDSBIN)\dclofficexp280.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
192192
</Excluded_Packages>
193+
<WelcomePageFile Path="README.md"/>
194+
<WelcomePageFolder/>
193195
</Delphi.Personality>
194196
<Deployment Version="4">
195197
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule">

0 commit comments

Comments
 (0)