|
| 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. |
0 commit comments