├── .gitignore
├── Images
└── CodeCoverage.ico
├── LICENSE
├── Packages
├── Delphi 10.2 Tokyo
│ ├── CodeCoverage.dpk
│ ├── CodeCoverage.dproj
│ └── CodeCoverage.prjmgc
├── Delphi 10.3 Rio
│ ├── CodeCoverage.dpk
│ ├── CodeCoverage.dproj
│ └── CodeCoverage.prjmgc
├── Delphi 10.4 Sydney
│ ├── CodeCoverage.dpk
│ ├── CodeCoverage.dproj
│ └── CodeCoverage.prjmgc
├── Delphi 11 Alexandria
│ ├── CodeCoverage.dpk
│ ├── CodeCoverage.dproj
│ └── CodeCoverage.prjmgc
└── Delphi 12 Athens
│ ├── CodeCoverage.dpk
│ ├── CodeCoverage.dproj
│ └── CodeCoverage.prjmgc
├── README.md
└── Source
├── CodeCoverage.ApiHelper.pas
├── CodeCoverage.Consts.pas
├── CodeCoverage.DM.dfm
├── CodeCoverage.DM.pas
├── CodeCoverage.Handler.pas
├── CodeCoverage.KeyBindings.pas
├── CodeCoverage.Main.pas
├── CodeCoverage.Notifier.pas
├── CodeCoverage.SyntaxTypes.pas
├── CodeCoverage.Tools.pas
├── CodeCoverage.Types.pas
├── DelphiAST
├── CodeCoverage.DelphiAST.Classes.pas
├── CodeCoverage.DelphiAST.Consts.pas
├── CodeCoverage.DelphiAST.SimpleParserEx.pas
├── CodeCoverage.DelphiAST.pas
├── CodeCoverage.SimpleParser.Lexer.Types.pas
├── CodeCoverage.SimpleParser.Lexer.pas
├── CodeCoverage.SimpleParser.Types.pas
├── CodeCoverage.SimpleParser.pas
└── SimpleParser.inc
├── HighDPI
├── CodeCoverage.Images.DM.dfm
└── CodeCoverage.Images.DM.pas
├── LowDPI
├── CodeCoverage.Images.DM.dfm
└── CodeCoverage.Images.DM.pas
├── PngComponents
├── CodeCoverage.PngFunctions.pas
└── CodeCoverage.PngImageList.pas
├── TestProject.dpr
├── TestProject.dproj
├── TestUnit1.pas
└── TestedUnit1.pas
/.gitignore:
--------------------------------------------------------------------------------
1 | # Uncomment these types if you want even more clean repository. But be careful.
2 | # It can make harm to an existing project source. Read explanations below.
3 | #
4 | # Resource files are binaries containing manifest, project icon and version info.
5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files.
6 | #*.res
7 | #
8 | # Type library file (binary). In old Delphi versions it should be stored.
9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored.
10 | #*.tlb
11 | #
12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7.
13 | # Uncomment this if you are not using diagrams or use newer Delphi version.
14 | #*.ddp
15 | #
16 | # Visual LiveBindings file. Added in Delphi XE2.
17 | # Uncomment this if you are not using LiveBindings Designer.
18 | #*.vlb
19 | #
20 | # Deployment Manager configuration file for your project. Added in Delphi XE2.
21 | # Uncomment this if it is not mobile development and you do not use remote debug feature.
22 | #*.deployproj
23 | #
24 | # C++ object files produced when C/C++ Output file generation is configured.
25 | # Uncomment this if you are not using external objects (zlib library for example).
26 | #*.obj
27 | #
28 |
29 | # Delphi compiler-generated binaries (safe to delete)
30 | *.exe
31 | *.dll
32 | *.bpl
33 | *.bpi
34 | *.dcp
35 | *.so
36 | *.apk
37 | *.drc
38 | *.map
39 | *.dres
40 | *.rsm
41 | *.tds
42 | *.dcu
43 | *.lib
44 | *.a
45 | *.o
46 | *.ocx
47 |
48 | # Delphi autogenerated files (duplicated info)
49 | *.cfg
50 | *.hpp
51 | *Resource.rc
52 |
53 | # Delphi local files (user-specific info)
54 | *.local
55 | *.identcache
56 | *.projdata
57 | *.tvsconfig
58 | *.dsk
59 |
60 | # Delphi history and backups
61 | __history/
62 | __recovery/
63 | *.~*
64 |
65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi)
66 | *.stat
67 |
68 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss
69 | modules/
70 |
--------------------------------------------------------------------------------
/Images/CodeCoverage.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UweRaabe/DelphiCodeCoveragePlugin/8f1246759e6ef6bb22c443c4e3b495d137319f98/Images/CodeCoverage.ico
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Uwe Raabe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Packages/Delphi 10.2 Tokyo/CodeCoverage.dpk:
--------------------------------------------------------------------------------
1 | package CodeCoverage;
2 |
3 | {$R *.res}
4 | {$R *.dres}
5 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
6 | {$ALIGN 8}
7 | {$ASSERTIONS ON}
8 | {$BOOLEVAL OFF}
9 | {$DEBUGINFO OFF}
10 | {$EXTENDEDSYNTAX ON}
11 | {$IMPORTEDDATA ON}
12 | {$IOCHECKS ON}
13 | {$LOCALSYMBOLS ON}
14 | {$LONGSTRINGS ON}
15 | {$OPENSTRINGS ON}
16 | {$OPTIMIZATION OFF}
17 | {$OVERFLOWCHECKS OFF}
18 | {$RANGECHECKS OFF}
19 | {$REFERENCEINFO ON}
20 | {$SAFEDIVIDE OFF}
21 | {$STACKFRAMES ON}
22 | {$TYPEDADDRESS OFF}
23 | {$VARSTRINGCHECKS ON}
24 | {$WRITEABLECONST OFF}
25 | {$MINENUMSIZE 1}
26 | {$IMAGEBASE $400000}
27 | {$DEFINE DEBUG}
28 | {$ENDIF IMPLICITBUILDING}
29 | {$DESCRIPTION 'Code Coverage Magician'}
30 | {$LIBSUFFIX '250'}
31 | {$DESIGNONLY}
32 | {$IMPLICITBUILD ON}
33 |
34 | requires
35 | DesignIDE;
36 |
37 | contains
38 | CodeCoverage.ApiHelper in '..\..\Source\CodeCoverage.ApiHelper.pas',
39 | CodeCoverage.Consts in '..\..\Source\CodeCoverage.Consts.pas',
40 | CodeCoverage.DM in '..\..\Source\CodeCoverage.DM.pas' {dmCodeCoverage: TDataModule},
41 | CodeCoverage.Handler in '..\..\Source\CodeCoverage.Handler.pas',
42 | CodeCoverage.KeyBindings in '..\..\Source\CodeCoverage.KeyBindings.pas',
43 | CodeCoverage.Main in '..\..\Source\CodeCoverage.Main.pas',
44 | CodeCoverage.Notifier in '..\..\Source\CodeCoverage.Notifier.pas',
45 | CodeCoverage.SyntaxTypes in '..\..\Source\CodeCoverage.SyntaxTypes.pas',
46 | CodeCoverage.Tools in '..\..\Source\CodeCoverage.Tools.pas',
47 | CodeCoverage.Types in '..\..\Source\CodeCoverage.Types.pas',
48 | CodeCoverage.Images.DM in '..\..\Source\LowDPI\CodeCoverage.Images.DM.pas' {dmCodeCoverageImages: TDataModule};
49 |
50 | end.
51 |
--------------------------------------------------------------------------------
/Packages/Delphi 10.2 Tokyo/CodeCoverage.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | True
4 | Package
5 | Release
6 | VCL
7 | CodeCoverage.dpk
8 | Win32
9 | {39F1CBBC-9BB2-4B75-B856-8E53C0E014D9}
10 | 18.4
11 | 1
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Cfg_1
29 | true
30 | true
31 |
32 |
33 | true
34 | Base
35 | true
36 |
37 |
38 | CodeCoverage
39 | All
40 | .\$(Platform)\$(Config)
41 | Code Coverage Magician
42 | .\$(Platform)\$(Config)
43 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;CodeCoverage;$(DCC_Namespace)
44 | ..\..\Source\DelphiAST;..\..\Source\PngComponents;$(DCC_UnitSearchPath)
45 | true
46 | 250
47 | true
48 | true
49 | 1031
50 |
51 |
52 | Debug
53 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
54 | CodeSiteLoggingPkg;DelphiASTPkg;PngComponents;$(DCC_UsePackage)
55 |
56 |
57 | true
58 | true
59 | DEBUG;$(DCC_Define)
60 | true
61 | false
62 | true
63 |
64 |
65 | false
66 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
67 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
68 | -rDebugIDE -pDelphi -np
69 |
70 |
71 | 0
72 | RELEASE;$(DCC_Define)
73 | false
74 | 0
75 |
76 |
77 |
78 | MainSource
79 |
80 |
81 |
82 |
83 |
84 |
85 | dfm
86 | TDataModule
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | dfm
98 | TDataModule
99 |
100 |
101 | ICON
102 | CodeCoverage
103 |
104 |
105 | Base
106 |
107 |
108 | Cfg_1
109 | Base
110 |
111 |
112 | Cfg_2
113 | Base
114 |
115 |
116 |
117 | Delphi.Personality.12
118 | Package
119 |
120 |
121 |
122 | CodeCoverage.dpk
123 |
124 |
125 |
126 |
127 | True
128 |
129 |
130 |
131 | (ToolsApi);WinApi;System.Win;System;Data;Vcl;Png*
132 |
133 |
134 |
135 | 12
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/Packages/Delphi 10.2 Tokyo/CodeCoverage.prjmgc:
--------------------------------------------------------------------------------
1 | [Settings]
2 | AutoLibSuffix=1
3 | ClearChildAppSettings=0
4 | ClearChildPackageSettings=1
5 | ClearChildVersionInfo=1
6 | NormalizeDproj=1
7 | SplitDproj=0
8 | EnableMissingPlatforms=0
9 | RemoveUnusedPlatforms=1
10 | RefreshFormType=1
11 | RemoveExcludedPackages=1
12 | RemoveDeployment=1
13 | RemoveUWP=1
14 |
15 |
--------------------------------------------------------------------------------
/Packages/Delphi 10.3 Rio/CodeCoverage.dpk:
--------------------------------------------------------------------------------
1 | package CodeCoverage;
2 |
3 | {$R *.res}
4 | {$R *.dres}
5 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
6 | {$ALIGN 8}
7 | {$ASSERTIONS ON}
8 | {$BOOLEVAL OFF}
9 | {$DEBUGINFO OFF}
10 | {$EXTENDEDSYNTAX ON}
11 | {$IMPORTEDDATA ON}
12 | {$IOCHECKS ON}
13 | {$LOCALSYMBOLS ON}
14 | {$LONGSTRINGS ON}
15 | {$OPENSTRINGS ON}
16 | {$OPTIMIZATION OFF}
17 | {$OVERFLOWCHECKS OFF}
18 | {$RANGECHECKS OFF}
19 | {$REFERENCEINFO ON}
20 | {$SAFEDIVIDE OFF}
21 | {$STACKFRAMES ON}
22 | {$TYPEDADDRESS OFF}
23 | {$VARSTRINGCHECKS ON}
24 | {$WRITEABLECONST OFF}
25 | {$MINENUMSIZE 1}
26 | {$IMAGEBASE $400000}
27 | {$DEFINE DEBUG}
28 | {$ENDIF IMPLICITBUILDING}
29 | {$DESCRIPTION 'Code Coverage Magician'}
30 | {$LIBSUFFIX '260'}
31 | {$DESIGNONLY}
32 | {$IMPLICITBUILD ON}
33 |
34 | requires
35 | DesignIDE;
36 |
37 | contains
38 | CodeCoverage.ApiHelper in '..\..\Source\CodeCoverage.ApiHelper.pas',
39 | CodeCoverage.Consts in '..\..\Source\CodeCoverage.Consts.pas',
40 | CodeCoverage.DM in '..\..\Source\CodeCoverage.DM.pas' {dmCodeCoverage: TDataModule},
41 | CodeCoverage.Handler in '..\..\Source\CodeCoverage.Handler.pas',
42 | CodeCoverage.KeyBindings in '..\..\Source\CodeCoverage.KeyBindings.pas',
43 | CodeCoverage.Main in '..\..\Source\CodeCoverage.Main.pas',
44 | CodeCoverage.Notifier in '..\..\Source\CodeCoverage.Notifier.pas',
45 | CodeCoverage.SyntaxTypes in '..\..\Source\CodeCoverage.SyntaxTypes.pas',
46 | CodeCoverage.Tools in '..\..\Source\CodeCoverage.Tools.pas',
47 | CodeCoverage.Types in '..\..\Source\CodeCoverage.Types.pas',
48 | CodeCoverage.Images.DM in '..\..\Source\LowDPI\CodeCoverage.Images.DM.pas' {dmCodeCoverageImages: TDataModule};
49 |
50 | end.
51 |
--------------------------------------------------------------------------------
/Packages/Delphi 10.3 Rio/CodeCoverage.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | True
4 | Package
5 | Release
6 | VCL
7 | CodeCoverage.dpk
8 | Win32
9 | {39F1CBBC-9BB2-4B75-B856-8E53C0E014D9}
10 | 18.8
11 | 1
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Cfg_1
29 | true
30 | true
31 |
32 |
33 | true
34 | Base
35 | true
36 |
37 |
38 | CodeCoverage
39 | All
40 | .\$(Platform)\$(Config)
41 | Code Coverage Magician
42 | .\$(Platform)\$(Config)
43 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;CodeCoverage;$(DCC_Namespace)
44 | ..\..\Source\DelphiAST;..\..\Source\PngComponents;$(DCC_UnitSearchPath)
45 | true
46 | 260
47 | true
48 | true
49 | 1031
50 |
51 |
52 | Debug
53 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
54 | CodeSiteLoggingPkg;DelphiASTPkg;PngComponents;$(DCC_UsePackage)
55 |
56 |
57 | true
58 | true
59 | DEBUG;$(DCC_Define)
60 | true
61 | false
62 | true
63 |
64 |
65 | false
66 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
67 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
68 | -rDebugIDE -pDelphi -np
69 |
70 |
71 | 0
72 | RELEASE;$(DCC_Define)
73 | false
74 | 0
75 |
76 |
77 |
78 | MainSource
79 |
80 |
81 |
82 |
83 |
84 |
85 | dfm
86 | TDataModule
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | dfm
98 | TDataModule
99 |
100 |
101 | ICON
102 | CodeCoverage
103 |
104 |
105 | Base
106 |
107 |
108 | Cfg_1
109 | Base
110 |
111 |
112 | Cfg_2
113 | Base
114 |
115 |
116 |
117 | Delphi.Personality.12
118 | Package
119 |
120 |
121 |
122 | CodeCoverage.dpk
123 |
124 |
125 |
126 |
127 | True
128 |
129 |
130 |
131 | (ToolsApi);WinApi;System.Win;System;Data;Vcl;Png*
132 |
133 |
134 |
135 | 12
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/Packages/Delphi 10.3 Rio/CodeCoverage.prjmgc:
--------------------------------------------------------------------------------
1 | [Settings]
2 | AutoLibSuffix=1
3 | ClearChildAppSettings=0
4 | ClearChildPackageSettings=1
5 | ClearChildVersionInfo=1
6 | NormalizeDproj=1
7 | SplitDproj=0
8 | EnableMissingPlatforms=0
9 | RemoveUnusedPlatforms=1
10 | RefreshFormType=1
11 | RemoveExcludedPackages=1
12 | RemoveDeployment=1
13 | RemoveUWP=1
14 |
15 |
--------------------------------------------------------------------------------
/Packages/Delphi 10.4 Sydney/CodeCoverage.dpk:
--------------------------------------------------------------------------------
1 | package CodeCoverage;
2 |
3 | {$R *.res}
4 | {$R *.dres}
5 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
6 | {$ALIGN 8}
7 | {$ASSERTIONS ON}
8 | {$BOOLEVAL OFF}
9 | {$DEBUGINFO OFF}
10 | {$EXTENDEDSYNTAX ON}
11 | {$IMPORTEDDATA ON}
12 | {$IOCHECKS ON}
13 | {$LOCALSYMBOLS ON}
14 | {$LONGSTRINGS ON}
15 | {$OPENSTRINGS ON}
16 | {$OPTIMIZATION OFF}
17 | {$OVERFLOWCHECKS OFF}
18 | {$RANGECHECKS OFF}
19 | {$REFERENCEINFO ON}
20 | {$SAFEDIVIDE OFF}
21 | {$STACKFRAMES ON}
22 | {$TYPEDADDRESS OFF}
23 | {$VARSTRINGCHECKS ON}
24 | {$WRITEABLECONST OFF}
25 | {$MINENUMSIZE 1}
26 | {$IMAGEBASE $400000}
27 | {$DEFINE DEBUG}
28 | {$ENDIF IMPLICITBUILDING}
29 | {$DESCRIPTION 'Code Coverage Magician'}
30 | {$LIBSUFFIX '270'}
31 | {$DESIGNONLY}
32 | {$IMPLICITBUILD ON}
33 |
34 | requires
35 | DesignIDE;
36 |
37 | contains
38 | CodeCoverage.ApiHelper in '..\..\Source\CodeCoverage.ApiHelper.pas',
39 | CodeCoverage.Consts in '..\..\Source\CodeCoverage.Consts.pas',
40 | CodeCoverage.DM in '..\..\Source\CodeCoverage.DM.pas' {dmCodeCoverage: TDataModule},
41 | CodeCoverage.Handler in '..\..\Source\CodeCoverage.Handler.pas',
42 | CodeCoverage.KeyBindings in '..\..\Source\CodeCoverage.KeyBindings.pas',
43 | CodeCoverage.Main in '..\..\Source\CodeCoverage.Main.pas',
44 | CodeCoverage.Notifier in '..\..\Source\CodeCoverage.Notifier.pas',
45 | CodeCoverage.SyntaxTypes in '..\..\Source\CodeCoverage.SyntaxTypes.pas',
46 | CodeCoverage.Tools in '..\..\Source\CodeCoverage.Tools.pas',
47 | CodeCoverage.Types in '..\..\Source\CodeCoverage.Types.pas',
48 | CodeCoverage.Images.DM in '..\..\Source\LowDPI\CodeCoverage.Images.DM.pas' {dmCodeCoverageImages: TDataModule};
49 |
50 | end.
51 |
--------------------------------------------------------------------------------
/Packages/Delphi 10.4 Sydney/CodeCoverage.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | True
4 | Package
5 | Release
6 | VCL
7 | CodeCoverage.dpk
8 | Win32
9 | {39F1CBBC-9BB2-4B75-B856-8E53C0E014D9}
10 | 19.2
11 | 1
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Cfg_1
29 | true
30 | true
31 |
32 |
33 | true
34 | Base
35 | true
36 |
37 |
38 | CodeCoverage
39 | All
40 | .\$(Platform)\$(Config)
41 | Code Coverage Magician
42 | .\$(Platform)\$(Config)
43 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;CodeCoverage;$(DCC_Namespace)
44 | ..\..\Source\DelphiAST;..\..\Source\PngComponents;$(DCC_UnitSearchPath)
45 | true
46 | 270
47 | true
48 | true
49 | 1031
50 |
51 |
52 | Debug
53 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
54 | CodeSiteLoggingPkg;DelphiASTPkg;PngComponents;$(DCC_UsePackage)
55 |
56 |
57 | true
58 | true
59 | DEBUG;$(DCC_Define)
60 | true
61 | false
62 | true
63 |
64 |
65 | false
66 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
67 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
68 | -rDebugIDE -pDelphi -np
69 |
70 |
71 | 0
72 | RELEASE;$(DCC_Define)
73 | false
74 | 0
75 |
76 |
77 |
78 | MainSource
79 |
80 |
81 |
82 |
83 |
84 |
85 | dfm
86 | TDataModule
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | dfm
98 | TDataModule
99 |
100 |
101 | ICON
102 | CodeCoverage
103 |
104 |
105 | Base
106 |
107 |
108 | Cfg_1
109 | Base
110 |
111 |
112 | Cfg_2
113 | Base
114 |
115 |
116 |
117 | Delphi.Personality.12
118 | Package
119 |
120 |
121 |
122 | CodeCoverage.dpk
123 |
124 |
125 |
126 |
127 | True
128 | False
129 |
130 |
131 |
132 | (ToolsApi);WinApi;System.Win;System;Data;Vcl;Png*
133 |
134 |
135 |
136 | 12
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/Packages/Delphi 10.4 Sydney/CodeCoverage.prjmgc:
--------------------------------------------------------------------------------
1 | [Settings]
2 | AutoLibSuffix=1
3 | ClearChildAppSettings=0
4 | ClearChildPackageSettings=1
5 | ClearChildVersionInfo=1
6 | NormalizeDproj=1
7 | SplitDproj=0
8 | EnableMissingPlatforms=0
9 | RemoveUnusedPlatforms=1
10 | RefreshFormType=1
11 | RemoveExcludedPackages=1
12 | RemoveDeployment=1
13 | RemoveUWP=1
14 |
15 |
--------------------------------------------------------------------------------
/Packages/Delphi 11 Alexandria/CodeCoverage.dpk:
--------------------------------------------------------------------------------
1 | package CodeCoverage;
2 |
3 | {$R *.res}
4 | {$R *.dres}
5 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
6 | {$ALIGN 8}
7 | {$ASSERTIONS ON}
8 | {$BOOLEVAL OFF}
9 | {$DEBUGINFO OFF}
10 | {$EXTENDEDSYNTAX ON}
11 | {$IMPORTEDDATA ON}
12 | {$IOCHECKS ON}
13 | {$LOCALSYMBOLS ON}
14 | {$LONGSTRINGS ON}
15 | {$OPENSTRINGS ON}
16 | {$OPTIMIZATION OFF}
17 | {$OVERFLOWCHECKS OFF}
18 | {$RANGECHECKS OFF}
19 | {$REFERENCEINFO ON}
20 | {$SAFEDIVIDE OFF}
21 | {$STACKFRAMES ON}
22 | {$TYPEDADDRESS OFF}
23 | {$VARSTRINGCHECKS ON}
24 | {$WRITEABLECONST OFF}
25 | {$MINENUMSIZE 1}
26 | {$IMAGEBASE $400000}
27 | {$DEFINE DEBUG}
28 | {$ENDIF IMPLICITBUILDING}
29 | {$DESCRIPTION 'Code Coverage Magician'}
30 | {$LIBSUFFIX '280'}
31 | {$DESIGNONLY}
32 | {$IMPLICITBUILD ON}
33 |
34 | requires
35 | DesignIDE;
36 |
37 | contains
38 | CodeCoverage.ApiHelper in '..\..\Source\CodeCoverage.ApiHelper.pas',
39 | CodeCoverage.Consts in '..\..\Source\CodeCoverage.Consts.pas',
40 | CodeCoverage.DM in '..\..\Source\CodeCoverage.DM.pas' {dmCodeCoverage: TDataModule},
41 | CodeCoverage.Handler in '..\..\Source\CodeCoverage.Handler.pas',
42 | CodeCoverage.KeyBindings in '..\..\Source\CodeCoverage.KeyBindings.pas',
43 | CodeCoverage.Main in '..\..\Source\CodeCoverage.Main.pas',
44 | CodeCoverage.Notifier in '..\..\Source\CodeCoverage.Notifier.pas',
45 | CodeCoverage.SyntaxTypes in '..\..\Source\CodeCoverage.SyntaxTypes.pas',
46 | CodeCoverage.Tools in '..\..\Source\CodeCoverage.Tools.pas',
47 | CodeCoverage.Types in '..\..\Source\CodeCoverage.Types.pas',
48 | CodeCoverage.Images.DM in '..\..\Source\HighDPI\CodeCoverage.Images.DM.pas' {dmCodeCoverageImages: TDataModule};
49 |
50 | end.
51 |
--------------------------------------------------------------------------------
/Packages/Delphi 11 Alexandria/CodeCoverage.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | True
4 | Package
5 | Debug
6 | VCL
7 | CodeCoverage.dpk
8 | Win32
9 | {39F1CBBC-9BB2-4B75-B856-8E53C0E014D9}
10 | 19.3
11 | 1
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Cfg_1
29 | true
30 | true
31 |
32 |
33 | true
34 | Base
35 | true
36 |
37 |
38 | CodeCoverage
39 | All
40 | .\$(Platform)\$(Config)
41 | Code Coverage Magician
42 | .\$(Platform)\$(Config)
43 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;CodeCoverage;$(DCC_Namespace)
44 | ..\..\Source\DelphiAST;..\..\Source\PngComponents;$(DCC_UnitSearchPath)
45 | true
46 | 280
47 | true
48 | true
49 | 1031
50 |
51 |
52 | Debug
53 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
54 | CodeSiteLoggingPkg;DelphiASTPkg;PngComponents;$(DCC_UsePackage)
55 |
56 |
57 | true
58 | true
59 | DEBUG;$(DCC_Define)
60 | true
61 | false
62 | true
63 |
64 |
65 | false
66 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
67 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
68 | -rDebugIDE -pDelphi -np
69 |
70 |
71 | 0
72 | RELEASE;$(DCC_Define)
73 | false
74 | 0
75 |
76 |
77 |
78 | MainSource
79 |
80 |
81 |
82 |
83 |
84 |
85 | TDataModule
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | TDataModule
97 |
98 |
99 | ICON
100 | CodeCoverage
101 |
102 |
103 | Base
104 |
105 |
106 | Cfg_1
107 | Base
108 |
109 |
110 | Cfg_2
111 | Base
112 |
113 |
114 |
115 | Delphi.Personality.12
116 | Package
117 |
118 |
119 |
120 | CodeCoverage.dpk
121 |
122 |
123 |
124 |
125 | True
126 | False
127 |
128 |
129 |
130 | (ToolsApi);WinApi;System.Win;System;Data;Vcl;Png*
131 |
132 |
133 |
134 | 12
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/Packages/Delphi 11 Alexandria/CodeCoverage.prjmgc:
--------------------------------------------------------------------------------
1 | [Settings]
2 | AutoLibSuffix=1
3 | ClearChildAppSettings=0
4 | ClearChildPackageSettings=1
5 | ClearChildVersionInfo=1
6 | NormalizeDproj=1
7 | SplitDproj=0
8 | EnableMissingPlatforms=0
9 | RemoveUnusedPlatforms=1
10 | RefreshFormType=1
11 | RemoveExcludedPackages=1
12 | RemoveDeployment=1
13 | RemoveUWP=1
14 |
15 |
--------------------------------------------------------------------------------
/Packages/Delphi 12 Athens/CodeCoverage.dpk:
--------------------------------------------------------------------------------
1 | package CodeCoverage;
2 |
3 | {$R *.res}
4 | {$R *.dres}
5 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
6 | {$ALIGN 8}
7 | {$ASSERTIONS ON}
8 | {$BOOLEVAL OFF}
9 | {$DEBUGINFO OFF}
10 | {$EXTENDEDSYNTAX ON}
11 | {$IMPORTEDDATA ON}
12 | {$IOCHECKS ON}
13 | {$LOCALSYMBOLS ON}
14 | {$LONGSTRINGS ON}
15 | {$OPENSTRINGS ON}
16 | {$OPTIMIZATION OFF}
17 | {$OVERFLOWCHECKS OFF}
18 | {$RANGECHECKS OFF}
19 | {$REFERENCEINFO ON}
20 | {$SAFEDIVIDE OFF}
21 | {$STACKFRAMES ON}
22 | {$TYPEDADDRESS OFF}
23 | {$VARSTRINGCHECKS ON}
24 | {$WRITEABLECONST OFF}
25 | {$MINENUMSIZE 1}
26 | {$IMAGEBASE $400000}
27 | {$DEFINE DEBUG}
28 | {$ENDIF IMPLICITBUILDING}
29 | {$DESCRIPTION 'Code Coverage Magician'}
30 | {$LIBSUFFIX '290'}
31 | {$DESIGNONLY}
32 | {$IMPLICITBUILD ON}
33 |
34 | requires
35 | DesignIDE;
36 |
37 | contains
38 | CodeCoverage.ApiHelper in '..\..\Source\CodeCoverage.ApiHelper.pas',
39 | CodeCoverage.Consts in '..\..\Source\CodeCoverage.Consts.pas',
40 | CodeCoverage.DM in '..\..\Source\CodeCoverage.DM.pas' {dmCodeCoverage: TDataModule},
41 | CodeCoverage.Handler in '..\..\Source\CodeCoverage.Handler.pas',
42 | CodeCoverage.KeyBindings in '..\..\Source\CodeCoverage.KeyBindings.pas',
43 | CodeCoverage.Main in '..\..\Source\CodeCoverage.Main.pas',
44 | CodeCoverage.Notifier in '..\..\Source\CodeCoverage.Notifier.pas',
45 | CodeCoverage.SyntaxTypes in '..\..\Source\CodeCoverage.SyntaxTypes.pas',
46 | CodeCoverage.Tools in '..\..\Source\CodeCoverage.Tools.pas',
47 | CodeCoverage.Types in '..\..\Source\CodeCoverage.Types.pas',
48 | CodeCoverage.Images.DM in '..\..\Source\HighDPI\CodeCoverage.Images.DM.pas' {dmCodeCoverageImages: TDataModule};
49 |
50 | end.
51 |
--------------------------------------------------------------------------------
/Packages/Delphi 12 Athens/CodeCoverage.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | True
4 | Package
5 | Debug
6 | VCL
7 | CodeCoverage.dpk
8 | Win32
9 | {39F1CBBC-9BB2-4B75-B856-8E53C0E014D9}
10 | CodeCoverage
11 | 20.3
12 | 1
13 |
14 |
15 | true
16 |
17 |
18 | true
19 | Base
20 | true
21 |
22 |
23 | true
24 | Base
25 | true
26 |
27 |
28 | true
29 | Cfg_1
30 | true
31 | true
32 |
33 |
34 | true
35 | Base
36 | true
37 |
38 |
39 | CodeCoverage
40 | All
41 | .\$(Platform)\$(Config)
42 | Code Coverage Magician
43 | .\$(Platform)\$(Config)
44 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;CodeCoverage;$(DCC_Namespace)
45 | ..\..\Source\DelphiAST;..\..\Source\PngComponents;$(DCC_UnitSearchPath)
46 | true
47 | 290
48 | true
49 | true
50 | 1031
51 |
52 |
53 | Debug
54 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
55 | CodeSiteLoggingPkg;DelphiASTPkg;PngComponents;$(DCC_UsePackage)
56 |
57 |
58 | true
59 | true
60 | DEBUG;$(DCC_Define)
61 | true
62 | false
63 | true
64 |
65 |
66 | false
67 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
68 | C:\Program Files (x86)\Embarcadero\Studio\19.0\bin\bds.exe
69 | -rDebugIDE -pDelphi -np
70 |
71 |
72 | 0
73 | RELEASE;$(DCC_Define)
74 | false
75 | 0
76 |
77 |
78 |
79 | MainSource
80 |
81 |
82 |
83 |
84 |
85 |
86 | dfm
87 | TDataModule
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | dfm
99 | TDataModule
100 |
101 |
102 | ICON
103 | CodeCoverage
104 |
105 |
106 | Base
107 |
108 |
109 | Cfg_1
110 | Base
111 |
112 |
113 | Cfg_2
114 | Base
115 |
116 |
117 |
118 | Delphi.Personality.12
119 | Package
120 |
121 |
122 |
123 | CodeCoverage.dpk
124 |
125 |
126 |
127 |
128 | True
129 | False
130 | False
131 |
132 |
133 |
134 | (ToolsApi);WinApi;System.Win;System;Data;Vcl;Png*
135 |
136 |
137 |
138 |
139 | Win64,Win64x,Win32
140 |
141 |
142 |
143 | 12
144 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/Packages/Delphi 12 Athens/CodeCoverage.prjmgc:
--------------------------------------------------------------------------------
1 | [Settings]
2 | AutoLibSuffix=1
3 | ClearChildAppSettings=0
4 | ClearChildPackageSettings=1
5 | ClearChildVersionInfo=1
6 | NormalizeDproj=1
7 | SplitDproj=0
8 | EnableMissingPlatforms=0
9 | RemoveUnusedPlatforms=1
10 | RefreshFormType=1
11 | RemoveExcludedPackages=1
12 | RemoveDeployment=1
13 | RemoveUWP=1
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DelphiCodeCoveragePlugin
2 | Delphi Code Coverage Plugin
3 |
4 | The DelphiCode Coverage Plugin adds code coverage to teh Delphi IDE. It supports Delphi 10.2 Tokyo, Delphi 10.3 Rio, Delphi 10.4 Sydney, Delphi 11 Alexandria and Delphi 12 Athens.
5 |
6 | To install the plugin open the appropriate package for your Delpih version, compile and install it into the IDE. Due to a display problem of the main toolbar a restart of the IDE is suggested. After that you should see two code coverage buttons:
7 |
8 |  Run Code Coverage: starts the test project in code coverage mode and displays the results.
9 |
10 |  Switch Code Coverage: enables or disables code coverage for the current method (where the cursor is in).
11 |
12 |
13 | The plugin is meant for Test Driven Development (TDD) to control which code parts are covered by tests and which are not.
14 |
15 | For a demonstration after installing the plugin, open the TestProject from the source folder. It consists of TestedUnit1, which declares a class TTestedClass with two methods to test. TestUnit1 contains the test class for DUnitX. The test class has two tests, each for one method in TTestedClass, and several test cases with different parameters for the tested methods. Initially only the first test case of each test is enabled, the others are commented out.
16 | Now open TestedUnit and place the cursor inside the implementation of TestedMethod1. Click on the Switch Code Coverage button in the toolbar or press Ctrl-Alt-F5. If code coverage for a method is enabled the code coverage symbol appears in the gutter left of the method header. Switching code coverage also works when the cursor is placed inside the method declaration in the class.
17 |
18 | 
19 |
20 |
21 | Click the Run Code Coverage toolbar button. The test programm will run and at the end the code coverage will be displayed.
22 |
23 | 
24 |
25 | Right to the method header you can see the code coverage of the method in percent (lines executed vs. total lines). The number in brackets behind each line shows the number of executions of that line. The red arrow after a line shows a line never executed. Little circles in the first column of each code line give a quick overview which lines are executed (filled blue circle), which are not executed (filled red circle) and which cannot be executed (hollow blue circle). The last also have no blue debugger dots in the gutter.
26 |
27 | Uncommenting the different test cases will result in more code covered. The goal is to have as much test cases to get 100% code coverage.
28 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.ApiHelper.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.ApiHelper;
2 |
3 | interface
4 |
5 | uses
6 | System.Generics.Collections, System.Classes, System.SysUtils,
7 | Vcl.Menus,
8 | ToolsAPI, PlatformAPI;
9 |
10 | type
11 | OTA = record
12 | strict private
13 | class var
14 | FIDELibSuffix: string;
15 | public
16 | class function AboutBoxServices: IOTAAboutBoxServices; static;
17 | class function DebuggerServices: IOTADebuggerServices; static;
18 | class function EditorServices: IOTAEditorServices; static;
19 | class function IDELibSuffix: string; static;
20 | class function IsPackageInstalled(const APackageName: string): Boolean; static;
21 | class function KeyboardServices: IOTAKeyboardServices; static;
22 | class function ModuleServices: IOTAModuleServices; static;
23 | class function PackageServices: IOTAPackageServices; static;
24 | class function PlatformServices: IOTAPlatformServices; static;
25 | class function Services: IOTAServices; static;
26 | end;
27 |
28 | NTA = record
29 | public
30 | class function EnvironmentOptionsServices: INTAEnvironmentOptionsServices; static;
31 | class function FindMenuItem(const AName: string): TMenuItem; overload; static;
32 | class function FindMenuItem(const BreadCrumbs: array of string): TMenuItem; overload; static;
33 | class function Services: INTAServices; static;
34 | end;
35 |
36 | type
37 | TCommonNotifier = class(TInterfacedObject, IOTANotifier)
38 | private
39 | FID: Integer;
40 | procedure Unregister;
41 | protected
42 | { IOTANotifier }
43 | procedure AfterSave;
44 | procedure BeforeSave;
45 | procedure Destroyed;
46 | procedure Modified;
47 |
48 | function DoRegister: Integer; virtual;
49 | procedure DoUnregister(ID: Integer); virtual;
50 | procedure InternalAfterSave; virtual;
51 | procedure InternalBeforeSave; virtual;
52 | procedure InternalDestroyed; virtual;
53 | procedure InternalModified; virtual;
54 | public
55 | constructor Create;
56 | destructor Destroy; override;
57 | procedure AfterConstruction; override;
58 | procedure Release;
59 | end;
60 |
61 | TNotifierHost = class(TInterfacedPersistent)
62 | private
63 | FNotifiers: TList;
64 | protected
65 | property Notifiers: TList read FNotifiers;
66 | public
67 | constructor Create;
68 | destructor Destroy; override;
69 | procedure AddNotifier(Value: TCommonNotifier);
70 | procedure ClearNotifiers;
71 | function FindNotifier(Predicate: TPredicate): Boolean; overload;
72 | function FindNotifier(Predicate: TPredicate; out Instance: T): Boolean; overload;
73 | procedure RemoveNotifier(Value: TCommonNotifier);
74 | end;
75 |
76 | THostedNotifier = class(TCommonNotifier)
77 | private
78 | FNotifierHost: TNotifierHost;
79 | protected
80 | procedure CheckNotifierHost(ANotifierHost: TNotifierHost); virtual;
81 | property NotifierHost: TNotifierHost read FNotifierHost;
82 | public
83 | constructor Create(ANotifierHost: TNotifierHost); overload;
84 | destructor Destroy; override;
85 | end;
86 |
87 | implementation
88 |
89 | uses
90 | System.IOUtils;
91 |
92 | type
93 | TMenuItemHelper = class helper for TMenuItem
94 | public
95 | function FindByName(const AName: string): TMenuItem;
96 | end;
97 |
98 | procedure TCommonNotifier.AfterConstruction;
99 | begin
100 | inherited;
101 | FID := DoRegister;
102 | end;
103 |
104 | procedure TCommonNotifier.AfterSave;
105 | begin
106 | InternalAfterSave;
107 | end;
108 |
109 | procedure TCommonNotifier.BeforeSave;
110 | begin
111 | InternalBeforeSave;
112 | end;
113 |
114 | constructor TCommonNotifier.Create;
115 | begin
116 | inherited;
117 | FID := -1;
118 | end;
119 |
120 | destructor TCommonNotifier.Destroy;
121 | begin
122 | Unregister;
123 | inherited;
124 | end;
125 |
126 | procedure TCommonNotifier.Destroyed;
127 | begin
128 | InternalDestroyed;
129 | end;
130 |
131 | function TCommonNotifier.DoRegister: Integer;
132 | begin
133 | Result := -1;
134 | end;
135 |
136 | procedure TCommonNotifier.Modified;
137 | begin
138 | InternalModified;
139 | end;
140 |
141 | procedure TCommonNotifier.DoUnregister(ID: Integer);
142 | begin
143 | end;
144 |
145 | procedure TCommonNotifier.InternalAfterSave;
146 | begin
147 | end;
148 |
149 | procedure TCommonNotifier.InternalBeforeSave;
150 | begin
151 | end;
152 |
153 | procedure TCommonNotifier.InternalDestroyed;
154 | begin
155 | Unregister;
156 | end;
157 |
158 | procedure TCommonNotifier.InternalModified;
159 | begin
160 | end;
161 |
162 | procedure TCommonNotifier.Release;
163 | begin
164 | Unregister;
165 | end;
166 |
167 | procedure TCommonNotifier.Unregister;
168 | var
169 | tmpID: Integer;
170 | begin
171 | if FID >= 0 then begin
172 | { to avoid recursive call }
173 | tmpID := FID;
174 | FID := -1;
175 | DoUnregister(tmpID);
176 | end;
177 | end;
178 |
179 | function TMenuItemHelper.FindByName(const AName: string): TMenuItem;
180 | var
181 | item: TMenuItem;
182 | begin
183 | for item in Self do begin
184 | if SameText(item.Name, AName) then
185 | Exit(item);
186 | Result := item.FindByName(AName);
187 | if Result <> nil then
188 | Exit;
189 | end;
190 | Result := nil;
191 | end;
192 |
193 | constructor TNotifierHost.Create;
194 | begin
195 | inherited Create;
196 | FNotifiers := TList.Create();
197 | end;
198 |
199 | destructor TNotifierHost.Destroy;
200 | begin
201 | ClearNotifiers;
202 | FNotifiers.Free;
203 | inherited Destroy;
204 | end;
205 |
206 | procedure TNotifierHost.AddNotifier(Value: TCommonNotifier);
207 | begin
208 | FNotifiers.Add(Value);
209 | end;
210 |
211 | procedure TNotifierHost.ClearNotifiers;
212 | var
213 | I: Integer;
214 | item: TCommonNotifier;
215 | begin
216 | for I := FNotifiers.Count - 1 downto 0 do begin
217 | item := FNotifiers[I];
218 | FNotifiers.Delete(I);
219 | item.Release;
220 | end;
221 | end;
222 |
223 | function TNotifierHost.FindNotifier(Predicate: TPredicate): Boolean;
224 | var
225 | instance: T;
226 | begin
227 | Result := FindNotifier(Predicate, instance);
228 | end;
229 |
230 | function TNotifierHost.FindNotifier(Predicate: TPredicate; out Instance: T): Boolean;
231 | var
232 | notifier: TCommonNotifier;
233 | item: T;
234 | begin
235 | Result := False;
236 | Instance := nil;
237 | for notifier in Notifiers do begin
238 | if notifier is T then begin
239 | item := notifier as T;
240 | if Predicate(item) then begin
241 | Instance := Item;
242 | Result := True;
243 | Break;
244 | end;
245 | end;
246 | end;
247 | end;
248 |
249 | procedure TNotifierHost.RemoveNotifier(Value: TCommonNotifier);
250 | begin
251 | FNotifiers.Remove(Value);
252 | end;
253 |
254 | class function OTA.AboutBoxServices: IOTAAboutBoxServices;
255 | begin
256 | BorlandIDEServices.GetService(IOTAAboutBoxServices, Result);
257 | end;
258 |
259 | class function OTA.DebuggerServices: IOTADebuggerServices;
260 | begin
261 | BorlandIDEServices.GetService(IOTADebuggerServices, Result);
262 | end;
263 |
264 | class function OTA.EditorServices: IOTAEditorServices;
265 | begin
266 | BorlandIDEServices.GetService(IOTAEditorServices, Result);
267 | end;
268 |
269 | class function OTA.IDELibSuffix: string;
270 | var
271 | I: Integer;
272 | myInfo: IOTAPackageInfo;
273 | begin
274 | if FIDELibSuffix = '' then begin
275 | { Extract LibSuffix from RTL package }
276 | for I := 0 to PackageServices.PackageCount - 1 do
277 | begin
278 | myInfo := PackageServices.Package[I];
279 | if SameText(myInfo.SymbolFileName, 'rtl') then begin // do not localize
280 | FIDELibSuffix := Copy(TPath.GetFileNameWithoutExtension(myInfo.Name), Length(myInfo.SymbolFileName) + 1);
281 | Break;
282 | end;
283 | end;
284 | end;
285 | Result := FIDELibSuffix;
286 | end;
287 |
288 | class function OTA.IsPackageInstalled(const APackageName: string): Boolean;
289 | var
290 | fullName: string;
291 | I: Integer;
292 | begin
293 | fullName := APackageName + IDELibSuffix + '.bpl'; // do not localize
294 | for I := 0 to PackageServices.PackageCount - 1 do begin
295 | if SameText(PackageServices.PackageNames[I], fullName) then Exit(True);
296 | end;
297 | Result := False;
298 | end;
299 |
300 | class function OTA.KeyboardServices: IOTAKeyboardServices;
301 | begin
302 | BorlandIDEServices.GetService(IOTAKeyboardServices, Result);
303 | end;
304 |
305 | class function OTA.ModuleServices: IOTAModuleServices;
306 | begin
307 | BorlandIDEServices.GetService(IOTAModuleServices, Result);
308 | end;
309 |
310 | class function OTA.PackageServices: IOTAPackageServices;
311 | begin
312 | BorlandIDEServices.GetService(IOTAPAckageServices, Result);
313 | end;
314 |
315 | class function OTA.PlatformServices: IOTAPlatformServices;
316 | begin
317 | BorlandIDEServices.GetService(IOTAPlatformServices, Result);
318 | end;
319 |
320 | class function OTA.Services: IOTAServices;
321 | begin
322 | BorlandIDEServices.GetService(IOTAServices, Result);
323 | end;
324 |
325 | class function NTA.EnvironmentOptionsServices: INTAEnvironmentOptionsServices;
326 | begin
327 | BorlandIDEServices.GetService(INTAEnvironmentOptionsServices, Result);
328 | end;
329 |
330 | class function NTA.FindMenuItem(const AName: string): TMenuItem;
331 | begin
332 | Result := Services.MainMenu.Items.FindByName(AName);
333 | end;
334 |
335 | class function NTA.FindMenuItem(const BreadCrumbs: array of string): TMenuItem;
336 | var
337 | item: TMenuItem;
338 | S: string;
339 | begin
340 | item := Services.MainMenu.Items;
341 | for S in BreadCrumbs do begin
342 | item := item.Find(S);
343 | if item = nil then Exit(nil);
344 | end;
345 | Result := item;
346 | end;
347 |
348 | class function NTA.Services: INTAServices;
349 | begin
350 | BorlandIDEServices.GetService(INTAServices, Result);
351 | end;
352 |
353 | constructor THostedNotifier.Create(ANotifierHost: TNotifierHost);
354 | begin
355 | CheckNotifierHost(ANotifierHost);
356 | inherited Create;
357 | FNotifierHost := ANotifierHost;
358 | FNotifierHost.AddNotifier(Self);
359 | end;
360 |
361 | destructor THostedNotifier.Destroy;
362 | begin
363 | FNotifierHost.RemoveNotifier(Self);
364 | inherited;
365 | end;
366 |
367 | procedure THostedNotifier.CheckNotifierHost(ANotifierHost: TNotifierHost);
368 | begin
369 | if ANotifierHost = nil then
370 | raise EProgrammerNotFound.Create('NotifierHost must not be nil!');
371 | end;
372 |
373 | end.
374 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.Consts.pas:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UweRaabe/DelphiCodeCoveragePlugin/8f1246759e6ef6bb22c443c4e3b495d137319f98/Source/CodeCoverage.Consts.pas
--------------------------------------------------------------------------------
/Source/CodeCoverage.DM.dfm:
--------------------------------------------------------------------------------
1 | object dmCodeCoverage: TdmCodeCoverage
2 | OldCreateOrder = True
3 | Height = 302
4 | Width = 584
5 | object Actions: TActionList
6 | Images = dmCodeCoverageImages.Images
7 | Left = 120
8 | Top = 40
9 | object actSwitchCodeCoverage: TAction
10 | Category = 'Code Coverage'
11 | Caption = 'Switch Code Coverage'
12 | Hint = 'Switch code coverage for current method'
13 | ImageIndex = 1
14 | OnExecute = actSwitchCodeCoverageExecute
15 | OnUpdate = actSwitchCodeCoverageUpdate
16 | end
17 | object actRunCodeCoverage: TAction
18 | Category = 'Code Coverage'
19 | Caption = 'Run Code Coverage'
20 | Hint = 'Runs the test project for code coverage'
21 | ImageIndex = 2
22 | OnExecute = actRunCodeCoverageExecute
23 | OnUpdate = actRunCodeCoverageUpdate
24 | end
25 | end
26 | object MenuItems: TPopupMenu
27 | Left = 296
28 | Top = 40
29 | object mnuRunCodeCoverage: TMenuItem
30 | Action = actRunCodeCoverage
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.DM.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.DM;
2 |
3 | interface
4 |
5 | uses
6 | System.Classes, System.Actions, System.Contnrs,
7 | Vcl.Graphics, Vcl.Menus, Vcl.ActnList, Vcl.ImgList,
8 | CodeCoverage.Handler;
9 |
10 | type
11 | TdmCodeCoverage = class(TDataModule)
12 | Actions: TActionList;
13 | actSwitchCodeCoverage: TAction;
14 | actRunCodeCoverage: TAction;
15 | MenuItems: TPopupMenu;
16 | mnuRunCodeCoverage: TMenuItem;
17 | procedure actRunCodeCoverageExecute(Sender: TObject);
18 | procedure actRunCodeCoverageUpdate(Sender: TObject);
19 | procedure actSwitchCodeCoverageExecute(Sender: TObject);
20 | procedure actSwitchCodeCoverageUpdate(Sender: TObject);
21 | private
22 | FCodeCoverage: TCodeCoverage;
23 | FActions: TComponentList;
24 | FImageIndexOffset: Integer;
25 | FMenuItems: TComponentList;
26 | procedure RememberActions;
27 | procedure RememberMenuItems;
28 | procedure SetCodeCoverage(const Value: TCodeCoverage);
29 | strict protected
30 | procedure ExecuteRunCodeCoverage;
31 | procedure ExecuteSwitchCodeCoverage;
32 | procedure UpdateRunCodeCoverage;
33 | procedure UpdateSwitchCodeCoverage;
34 | protected
35 | function GetImageList: TCustomImageList;
36 | public
37 | constructor Create(AOwner: TComponent); override;
38 | constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0); override;
39 | destructor Destroy; override;
40 | function FindImageIndexByName(const AImageName: string): Integer;
41 | function IsMyAction(Action: TBasicAction): Boolean;
42 | procedure RemoveActions;
43 | procedure RemoveMenuItems;
44 | property CodeCoverage: TCodeCoverage read FCodeCoverage write SetCodeCoverage;
45 | property ImageIndexOffset: Integer read FImageIndexOffset write FImageIndexOffset;
46 | property ImageList: TCustomImageList read GetImageList;
47 | end;
48 |
49 | var
50 | dmCodeCoverage: TdmCodeCoverage;
51 |
52 | implementation
53 |
54 | uses
55 | CodeCoverage.Images.DM;
56 |
57 | {%CLASSGROUP 'Vcl.Controls.TControl'}
58 |
59 | {$R *.dfm}
60 |
61 | constructor TdmCodeCoverage.Create(AOwner: TComponent);
62 | begin
63 | inherited Create(AOwner);
64 | FActions := TComponentList.Create(False);
65 | FMenuItems := TComponentList.Create(False);
66 | RememberActions;
67 | RememberMenuItems;
68 | end;
69 |
70 | constructor TdmCodeCoverage.CreateNew(AOwner: TComponent; Dummy: Integer);
71 | begin
72 | { avoids the datamodule being added to Screen.Datamodules }
73 | // Dummy := -1;
74 | inherited;
75 | end;
76 |
77 | destructor TdmCodeCoverage.Destroy;
78 | begin
79 | FMenuItems.Free;
80 | FActions.Free;
81 | inherited Destroy;
82 | end;
83 |
84 | procedure TdmCodeCoverage.actRunCodeCoverageExecute(Sender: TObject);
85 | begin
86 | ExecuteRunCodeCoverage;
87 | end;
88 |
89 | procedure TdmCodeCoverage.actRunCodeCoverageUpdate(Sender: TObject);
90 | begin
91 | UpdateRunCodeCoverage;
92 | end;
93 |
94 | procedure TdmCodeCoverage.actSwitchCodeCoverageExecute(Sender: TObject);
95 | begin
96 | ExecuteSwitchCodeCoverage;
97 | end;
98 |
99 | procedure TdmCodeCoverage.actSwitchCodeCoverageUpdate(Sender: TObject);
100 | begin
101 | UpdateSwitchCodeCoverage;
102 | end;
103 |
104 | function TdmCodeCoverage.IsMyAction(Action: TBasicAction): Boolean;
105 | begin
106 | Result := (FActions.IndexOf(Action) >= 0);
107 | end;
108 |
109 | procedure TdmCodeCoverage.RememberActions;
110 | var
111 | item: TContainedAction;
112 | begin
113 | for item in Actions do begin
114 | item.Tag := item.ImageIndex;
115 | FActions.Add(item);
116 | end;
117 | end;
118 |
119 | procedure TdmCodeCoverage.RememberMenuItems;
120 | var
121 | item: TMenuItem;
122 | begin
123 | for item in MenuItems.Items do begin
124 | FMenuItems.Add(item);
125 | end;
126 | end;
127 |
128 | procedure TdmCodeCoverage.RemoveActions;
129 | var
130 | I: Integer;
131 | begin
132 | for I := FActions.Count - 1 downto 0 do begin
133 | FActions[I].Free;
134 | end;
135 | end;
136 |
137 | procedure TdmCodeCoverage.RemoveMenuItems;
138 | var
139 | I: Integer;
140 | begin
141 | for I := FMenuItems.Count - 1 downto 0 do begin
142 | FMenuItems[I].Free;
143 | end;
144 | end;
145 |
146 | procedure TdmCodeCoverage.UpdateSwitchCodeCoverage;
147 | var
148 | state: TCoverState;
149 | begin
150 | state := TCoverState.noncoverable;
151 | if CodeCoverage <> nil then begin
152 | if CodeCoverage.IsAvailable then begin
153 | state := CodeCoverage.CurrentMethodState;
154 | end;
155 | end;
156 | actSwitchCodeCoverage.Enabled := (state > TCoverState.noncoverable);
157 | actSwitchCodeCoverage.Checked := (state = TCoverState.covered);
158 | end;
159 |
160 | procedure TdmCodeCoverage.UpdateRunCodeCoverage;
161 | var
162 | enabled: Boolean;
163 | begin
164 | enabled := False;
165 | if CodeCoverage <> nil then begin
166 | enabled := CodeCoverage.HasCodeCoverage;
167 | end;
168 | actRunCodeCoverage.Enabled := enabled;
169 | end;
170 |
171 | procedure TdmCodeCoverage.ExecuteRunCodeCoverage;
172 | begin
173 | if CodeCoverage <> nil then begin
174 | CodeCoverage.Execute;
175 | end;
176 | end;
177 |
178 | procedure TdmCodeCoverage.ExecuteSwitchCodeCoverage;
179 | begin
180 | if CodeCoverage <> nil then begin
181 | CodeCoverage.SwitchCodeCoverage;
182 | UpdateSwitchCodeCoverage;
183 | end;
184 | end;
185 |
186 | function TdmCodeCoverage.FindImageIndexByName(const AImageName: string): Integer;
187 | begin
188 | Result := dmCodeCoverageImages.FindImageIndexByName(AImageName);
189 | end;
190 |
191 | function TdmCodeCoverage.GetImageList: TCustomImageList;
192 | begin
193 | Result := Actions.Images;
194 | end;
195 |
196 | procedure TdmCodeCoverage.SetCodeCoverage(const Value: TCodeCoverage);
197 | begin
198 | if FCodeCoverage <> Value then
199 | begin
200 | FCodeCoverage := Value;
201 | if FCodeCoverage <> nil then begin
202 | FCodeCoverage.ImageList := ImageList;
203 | FCodeCoverage.ImageIndexCodeCoverage := FindImageIndexByName('CodeCoverage'); // do not localize
204 | FCodeCoverage.ImageIndexNoCoverage := FindImageIndexByName('NoCoverage'); // do not localize
205 | end;
206 | end;
207 | end;
208 |
209 | end.
210 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.Handler.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.Handler;
2 |
3 | interface
4 |
5 | uses
6 | ToolsAPI,
7 | System.Classes,
8 | Vcl.Menus, Vcl.Graphics, Vcl.ImgList,
9 | CodeCoverage.ApiHelper, CodeCoverage.Types, CodeCoverage.SyntaxTypes, CodeCoverage.Notifier;
10 |
11 | type
12 | {$SCOPEDENUMS ON}
13 | TCoverState = (noncoverable, coverable, covered);
14 | {$SCOPEDENUMS OFF}
15 |
16 | type
17 | TCodeCoverage = class(TNotifierHost, ICodeCoverage)
18 | private const
19 | cBreakpointGroupName = 'CodeCoverage';
20 | private
21 | FActive: Boolean;
22 | FCodeSyntaxTrees: TCodeSyntaxTrees;
23 | FCoveredLines: TCoveredLines;
24 | FCoveredMethods: TCoveredMethods;
25 | FCurFileName: string;
26 | FCurLineMax: Integer;
27 | FCurLineMin: Integer;
28 | FCurProcess: IOTAProcess;
29 | FCurrentMethodState: TCoverState;
30 | FFullRepaint: TStringList;
31 | FImageIndexCodeCoverage: Integer;
32 | FImageIndexNoCoverage: Integer;
33 | FImageList: TCustomImageList;
34 | FRunMenuItem: TMenuItem;
35 | FValid: Boolean;
36 | function AddBreakpoint(ALineNumber: Integer): IOTABreakpoint;
37 | procedure AddCoverage(const AFileName: string; ALineNumber, ACount: Integer);
38 | function AddLineTracker(const EditBuffer: IOTAEditBuffer; ALineNumber: Integer): Integer;
39 | procedure CalcCoverage(const AFileName: string; const Data: TCoveredMethod);
40 | procedure CheckCodeCoverage;
41 | function CheckFullRepaint(const EditView: IOTAEditView): Boolean;
42 | procedure ClearAllCodeCoverage;
43 | procedure CoverMethod(const AFileName: string; const Data: TCoveredMethod);
44 | function CreateEditLineNotifier(const Tracker: IOTAEditLineTracker): Integer;
45 | procedure CreateEditorNotifier(const Editor: IOTASourceEditor);
46 | procedure CreateEditViewNotifier(const View: IOTAEditView);
47 | procedure CreateModuleNotifier(const Module: IOTAModule);
48 | function CreateSyntaxTree(const Editor: IOTASourceEditor): TCodeSyntaxTree;
49 | procedure DrawImage(ACanvas: TCanvas; X, Y, Index: Integer);
50 | procedure EnableCodeCoverage(const AProcess: IOTAProcess);
51 | function FindCoveredLinesList(const EditView: IOTAEditView): TCoveredLinesList;
52 | function FindCoveredMethodList(const EditView: IOTAEditView): TCoveredMethodList;
53 | function FindEditLineNotifier(const Tracker: IOTAEditLineTracker): TEditLineNotifier;
54 | function FindMethod(const EditBuffer: IOTAEditBuffer; Line: Integer; out Data: TCoveredMethod): Boolean;
55 | function FindSourceEditor(const AFileName: string): IOTASourceEditor;
56 | function FindSyntaxTree(const AFileName: string): TCodeSyntaxTree; overload;
57 | function FindSyntaxTree(const Editor: IOTASourceEditor): TCodeSyntaxTree; overload;
58 | function GetHasCodeCoverage: Boolean;
59 | function GetImageIndexCodeCoverage: Integer;
60 | function GetImageIndexNoCoverage: Integer;
61 | function GetValid: Boolean;
62 | function HandleSourceLine(ALineNumber: Integer): Boolean;
63 | function HasNotifier(const Target: T): Boolean; overload;
64 | procedure MarkFullRepaint(const AFileName: string);
65 | procedure MarkModified(const EditView: IOTAEditView);
66 | function MethodIdByLineNumber(const EditBuffer: IOTAEditBuffer; Line: Integer; out AID: TMethodID): Boolean;
67 | procedure ModuleRenamed(const OldName: string; const NewName: string);
68 | procedure RemoveEditLineNotifier(const Tracker: IOTAEditLineTracker);
69 | procedure RemoveEditor(const Editor: IOTASourceEditor);
70 | procedure RemoveLineTracker(const EditBuffer: IOTAEditBuffer); overload;
71 | procedure RemoveLineTracker(const EditBuffer: IOTAEditBuffer; ALineNumber, AID: Integer); overload;
72 | procedure RetrieveResults;
73 | function SelectCurrentMethod(const EditBuffer: IOTAEditBuffer; out AFileName, AMethodName: string; out LineMin,
74 | LineMax: Integer): Boolean; overload;
75 | function SelectMethod(const AFileName: string; const AMethod: TCoveredMethod;
76 | out LineMin, LineMax: Integer): Boolean;
77 | procedure TrackedLineChanged(const Tracker: IOTAEditLineTracker; OldLine, NewLine, Data: Integer);
78 | procedure UpdateCurrentMethodState;
79 | protected
80 | property Active: Boolean read FActive write FActive;
81 | property CodeSyntaxTrees: TCodeSyntaxTrees read FCodeSyntaxTrees;
82 | property CoveredLines: TCoveredLines read FCoveredLines;
83 | property CoveredMethods: TCoveredMethods read FCoveredMethods;
84 | property CurFileName: string read FCurFileName write FCurFileName;
85 | property CurLineMax: Integer read FCurLineMax write FCurLineMax;
86 | property CurLineMin: Integer read FCurLineMin write FCurLineMin;
87 | property CurProcess: IOTAProcess read FCurProcess write FCurProcess;
88 | property FullRepaint: TStringList read FFullRepaint;
89 | property Valid: Boolean read GetValid write FValid;
90 | public
91 | constructor Create;
92 | destructor Destroy; override;
93 | procedure Execute;
94 | procedure Initialize;
95 | function IsAvailable: Boolean;
96 | function SwitchCodeCoverage: Boolean;
97 | property CurrentMethodState: TCoverState read FCurrentMethodState;
98 | property HasCodeCoverage: Boolean read GetHasCodeCoverage;
99 | property ImageIndexCodeCoverage: Integer read GetImageIndexCodeCoverage write FImageIndexCodeCoverage;
100 | property ImageIndexNoCoverage: Integer read GetImageIndexNoCoverage write FImageIndexNoCoverage;
101 | property ImageList: TCustomImageList read FImageList write FImageList;
102 | property RunMenuItem: TMenuItem read FRunMenuItem write FRunMenuItem;
103 | end;
104 |
105 | implementation
106 |
107 | uses
108 | System.Math, System.StrUtils, System.Types, System.SysUtils,
109 | Vcl.Dialogs;
110 |
111 | function GetSourceLines(LineNum: Integer; ClientArg: Pointer): Integer pascal;
112 | begin
113 | Result := 0;
114 | if TCodeCoverage(ClientArg).HandleSourceLine(LineNum) then begin
115 | Result := 1;
116 | end;
117 | end;
118 |
119 | constructor TCodeCoverage.Create;
120 | begin
121 | inherited Create;
122 | FCoveredLines := TCoveredLines.Create();
123 | FCoveredMethods := TCoveredMethods.Create();
124 | FCodeSyntaxTrees := TCodeSyntaxTrees.Create();
125 |
126 | FFullRepaint := TStringList.Create(dupIgnore, true, false);
127 |
128 | TDebuggerNotifier.Create(Self);
129 | TEditServicesNotifier.Create(Self);
130 |
131 | CreateEditViewNotifier(OTA.EditorServices.TopView);
132 | end;
133 |
134 | destructor TCodeCoverage.Destroy;
135 | begin
136 | FFullRepaint.Free;
137 | FCodeSyntaxTrees.Free;
138 | FCoveredMethods.Free;
139 | FCoveredLines.Free;
140 | inherited;
141 | end;
142 |
143 | procedure TCodeCoverage.DrawImage(ACanvas: TCanvas; X, Y, Index: Integer);
144 | begin
145 | ImageList.Draw(ACanvas, X, Y, Index);
146 | end;
147 |
148 | function TCodeCoverage.AddBreakpoint(ALineNumber: Integer): IOTABreakpoint;
149 | begin
150 | Result := OTA.DebuggerServices.NewSourceBreakpoint(CurFileName, ALineNumber, CurProcess);
151 | if Result <> nil then begin
152 | Result.DoBreak := false;
153 | Result.GroupName := cBreakpointGroupName;
154 | if ALineNumber = CurLineMin then begin
155 | Result.DoIgnoreExceptions := True;
156 | end
157 | else if ALineNumber = CurLineMax then begin
158 | Result.DoHandleExceptions := True;
159 | end
160 | else begin
161 | Result.PassCount := MaxInt;
162 | CoveredLines.Initialize(CurFileName, ALineNumber, 0);
163 | end;
164 | end;
165 | end;
166 |
167 | procedure TCodeCoverage.AddCoverage(const AFileName: string; ALineNumber, ACount: Integer);
168 | begin
169 | CoveredLines.Add(AFileName, ALineNumber, ACount);
170 | end;
171 |
172 | function TCodeCoverage.AddLineTracker(const EditBuffer: IOTAEditBuffer; ALineNumber: Integer): Integer;
173 | var
174 | Tracker: IOTAEditLineTracker;
175 | begin
176 | Tracker := EditBuffer.GetEditLineTracker;
177 | Result := CreateEditLineNotifier(Tracker);
178 | Tracker.AddLine(ALineNumber, Result);
179 | end;
180 |
181 | procedure TCodeCoverage.CalcCoverage(const AFileName: string; const Data: TCoveredMethod);
182 | var
183 | Count: Integer;
184 | covered: Integer;
185 | I: Integer;
186 | percent: Integer;
187 | total: Integer;
188 | begin
189 | total := 0;
190 | covered := 0;
191 | for I := Data.LineMin to Data.LineMax do begin
192 | Count := CoveredLines.Find(AFileName, I);
193 | if Count >= 0 then begin
194 | Inc(total);
195 | if Count > 0 then begin
196 | Inc(covered);
197 | end;
198 | end;
199 | end;
200 | if total = covered then begin
201 | percent := 100;
202 | end
203 | else if covered = 0 then begin
204 | percent := 0;
205 | end
206 | else begin
207 | percent := EnsureRange(Round(100*covered/total), 1, 99);
208 | end;
209 | CoveredMethods.UpdatePercent(AFileName, Data.ID, percent);
210 | end;
211 |
212 | procedure TCodeCoverage.CheckCodeCoverage;
213 | var
214 | bp: IOTABreakpoint;
215 | I: Integer;
216 | begin
217 | for I := 0 to OTA.DebuggerServices.SourceBkptCount - 1 do begin
218 | bp := OTA.DebuggerServices.SourceBkpts[I];
219 | if MatchStr(bp.GroupName, [cBreakpointGroupName]) then begin
220 | if not (bp.DoHandleExceptions or bp.DoIgnoreExceptions) then begin
221 | AddCoverage(bp.FileName, bp.LineNumber, bp.CurPassCount);
222 | end;
223 | end;
224 | end;
225 | CoveredMethods.Iterate(CalcCoverage);
226 |
227 | Valid := true;
228 | end;
229 |
230 | function TCodeCoverage.CheckFullRepaint(const EditView: IOTAEditView): Boolean;
231 | var
232 | idx: Integer;
233 | begin
234 | Result := FullRepaint.Find(EditView.Buffer.FileName, idx);
235 | if Result then begin
236 | FullRepaint.Delete(idx);
237 | end;
238 | end;
239 |
240 | procedure TCodeCoverage.ClearAllCodeCoverage;
241 | var
242 | bp: IOTABreakpoint;
243 | I: Integer;
244 | begin
245 | for I := OTA.DebuggerServices.SourceBkptCount - 1 downto 0 do begin
246 | bp := OTA.DebuggerServices.SourceBkpts[I];
247 | if bp.GroupName = cBreakpointGroupName then begin
248 | OTA.DebuggerServices.RemoveBreakpoint(bp);
249 | end;
250 | end;
251 |
252 | FCurProcess := nil;
253 | end;
254 |
255 | procedure TCodeCoverage.CoverMethod(const AFileName: string; const Data: TCoveredMethod);
256 | var
257 | I: Integer;
258 | begin
259 | if CurProcess.SourceIsDebuggable[AFileName] then begin
260 | if SelectMethod(AFileName, Data, FCurLineMin, FCurLineMax) then begin
261 | CoveredMethods.Update(AFileName, Data.ID, FCurLineMin + 1, FCurLineMax - 1);
262 | FCurFileName := AFileName;
263 | for I := FCurLineMin + 1 to FCurLineMax - 1 do begin
264 | CoveredLines.Initialize(FCurFileName, I, -1);
265 | end;
266 | CurProcess.GetSourceLines(FCurFileName, FCurLineMin, GetSourceLines, Self);
267 | end;
268 | end;
269 | end;
270 |
271 | function TCodeCoverage.CreateEditLineNotifier(const Tracker: IOTAEditLineTracker): Integer;
272 | var
273 | instance: TEditLineNotifier;
274 | begin
275 | if Tracker = nil then
276 | Exit(-1);
277 |
278 | { check if Tracker already has a notifier }
279 | instance := FindEditLineNotifier(Tracker);
280 | if instance = nil then begin
281 | instance := TEditLineNotifier.Create(Self, Tracker);
282 | end;
283 | Result := instance.NextID;
284 | end;
285 |
286 | procedure TCodeCoverage.CreateEditorNotifier(const Editor: IOTASourceEditor);
287 | begin
288 | if not HasNotifier(Editor) then begin
289 | TEditorNotifier.Create(Self, Editor);
290 | end;
291 | end;
292 |
293 | procedure TCodeCoverage.CreateEditViewNotifier(const View: IOTAEditView);
294 | begin
295 | if not HasNotifier(View) then begin
296 | TEditViewNotifier.Create(Self, View);
297 | end;
298 | end;
299 |
300 | procedure TCodeCoverage.CreateModuleNotifier(const Module: IOTAModule);
301 | begin
302 | if not HasNotifier(Module) then begin
303 | TModuleNotifier.Create(Self, Module);
304 | end;
305 | end;
306 |
307 | function TCodeCoverage.CreateSyntaxTree(const Editor: IOTASourceEditor): TCodeSyntaxTree;
308 | begin
309 | Result := CodeSyntaxTrees.Add(Editor);
310 | if Result <> nil then begin
311 | CreateEditorNotifier(Editor);
312 | CreateModuleNotifier(Editor.Module);
313 | end;
314 | end;
315 |
316 | procedure TCodeCoverage.EnableCodeCoverage(const AProcess: IOTAProcess);
317 | begin
318 | if not Active then
319 | Exit;
320 |
321 | CoveredLines.Clear;
322 | Valid := false;
323 | FCurProcess := AProcess;
324 | CoveredMethods.Iterate(CoverMethod);
325 | Active := not CoveredLines.IsEmpty;
326 | end;
327 |
328 | procedure TCodeCoverage.Execute;
329 | var
330 | builder: IOTAProjectBuilder;
331 | project: IOTAProject;
332 | begin
333 | project := OTA.ModuleServices.GetActiveProject;
334 | if project = nil then begin
335 | ShowMessage('No active project!');
336 | Exit;
337 | end;
338 |
339 | ClearAllCodeCoverage;
340 |
341 | builder := project.ProjectBuilder;
342 | if builder = nil then begin
343 | ShowMessage('Active project has no project buidler!');
344 | Exit;
345 | end;
346 |
347 | if builder.BuildProject(cmOTABuild, false, true) then begin
348 | // OTADebuggerServices.CreateProcess(project.ProjectOptions.TargetName, '');
349 | if (RunMenuItem <> nil) and RunMenuItem.Enabled then begin
350 | Active := true;
351 | try
352 | RunMenuItem.Click;
353 | except
354 | Active := false;
355 | end;
356 | end;
357 | end;
358 | end;
359 |
360 | function TCodeCoverage.FindCoveredLinesList(const EditView: IOTAEditView): TCoveredLinesList;
361 | begin
362 | Result := CoveredLines.Find(EditView.Buffer.FileName);
363 | end;
364 |
365 | function TCodeCoverage.FindCoveredMethodList(const EditView: IOTAEditView): TCoveredMethodList;
366 | begin
367 | Result := CoveredMethods.Find(EditView.Buffer.FileName);
368 | end;
369 |
370 | function TCodeCoverage.FindEditLineNotifier(const Tracker: IOTAEditLineTracker): TEditLineNotifier;
371 | var
372 | instance: TEditLineNotifier;
373 | begin
374 | Result := nil;
375 | if Tracker = nil then
376 | Exit;
377 |
378 |
379 | if FindNotifier(
380 | function(Arg: TEditLineNotifier): Boolean
381 | begin
382 | Result := Arg.HandlesTarget(Tracker);
383 | end,
384 | instance) then
385 | begin
386 | Result := instance;
387 | end;
388 | end;
389 |
390 | function TCodeCoverage.FindMethod(const EditBuffer: IOTAEditBuffer; Line: Integer; out Data: TCoveredMethod): Boolean;
391 | var
392 | curID: TMethodID;
393 | begin
394 | Result := MethodIdByLineNumber(EditBuffer, Line, curID) and CoveredMethods.Find(EditBuffer.FileName, curID, Data);
395 | end;
396 |
397 | function TCodeCoverage.FindSourceEditor(const AFileName: string): IOTASourceEditor;
398 | var
399 | Editor: IOTAEditor;
400 | I: Integer;
401 | module: IOTAModule;
402 | sourceEditor: IOTASourceEditor;
403 | begin
404 | Result := nil;
405 |
406 | module := OTA.ModuleServices.FindModule(AFileName);
407 | for I := 0 to module.ModuleFileCount - 1 do begin
408 | Editor := module.ModuleFileEditors[I];
409 | if Supports(Editor, IOTASourceEditor, sourceEditor) then begin
410 | Exit(sourceEditor);
411 | end;
412 | end;
413 | end;
414 |
415 | function TCodeCoverage.FindSyntaxTree(const AFileName: string): TCodeSyntaxTree;
416 | begin
417 | if AFileName = '' then
418 | Exit(nil);
419 |
420 | if not CodeSyntaxTrees.Find(AFileName, Result) then begin
421 | Result := CreateSyntaxTree(FindSourceEditor(AFileName));
422 | end;
423 | end;
424 |
425 | function TCodeCoverage.FindSyntaxTree(const Editor: IOTASourceEditor): TCodeSyntaxTree;
426 | begin
427 | if Editor = nil then
428 | Exit(nil);
429 |
430 | if not CodeSyntaxTrees.Find(Editor.FileName, Result) then begin
431 | Result := CreateSyntaxTree(Editor);
432 | end;
433 | end;
434 |
435 | function TCodeCoverage.GetHasCodeCoverage: Boolean;
436 | begin
437 | Result := IsAvailable and not CoveredMethods.IsEmpty;
438 | end;
439 |
440 | function TCodeCoverage.GetImageIndexCodeCoverage: Integer;
441 | begin
442 | Result := FImageIndexCodeCoverage;
443 | end;
444 |
445 | function TCodeCoverage.GetImageIndexNoCoverage: Integer;
446 | begin
447 | Result := FImageIndexNoCoverage;
448 | end;
449 |
450 | function TCodeCoverage.GetValid: Boolean;
451 | begin
452 | Result := FValid;
453 | end;
454 |
455 | function TCodeCoverage.HandleSourceLine(ALineNumber: Integer): Boolean;
456 | begin
457 | Result := false;
458 | if ALineNumber <= CurLineMax then begin
459 | AddBreakpoint(ALineNumber);
460 | Result := true;
461 | end;
462 | end;
463 |
464 | function TCodeCoverage.HasNotifier(const Target: T): Boolean;
465 | begin
466 | Result := FindNotifier>(
467 | function(Arg: TCodeCoverageNotifier): Boolean
468 | begin
469 | Result := Arg.HandlesTarget(Target);
470 | end);
471 | end;
472 |
473 | procedure TCodeCoverage.Initialize;
474 | var
475 | Editor: IOTAEditor;
476 | I: Integer;
477 | J: Integer;
478 | K: Integer;
479 | module: IOTAModule;
480 | moduleServices: IOTAModuleServices;
481 | sourceEditor: IOTASourceEditor;
482 | view: IOTAEditView;
483 | begin
484 | moduleServices := OTA.ModuleServices;
485 | for I := 0 to moduleServices.ModuleCount - 1 do begin
486 | module := moduleServices.Modules[I];
487 | for J := 0 to module.ModuleFileCount - 1 do begin
488 | Editor := module.ModuleFileEditors[J];
489 | if Supports(Editor, IOTASourceEditor, sourceEditor) then begin
490 | for K := 0 to sourceEditor.EditViewCount - 1 do begin
491 | view := sourceEditor.EditViews[K];
492 | CreateEditViewNotifier(view);
493 | end;
494 | end;
495 | end;
496 | end;
497 | UpdateCurrentMethodState;
498 | end;
499 |
500 | function TCodeCoverage.IsAvailable: Boolean;
501 | var
502 | project: IOTAProject;
503 | begin
504 | Result := False;
505 |
506 | project := OTA.ModuleServices.GetActiveProject;
507 | if project = nil then
508 | Exit;
509 | if not MatchText(project.ApplicationType, [sApplication, sConsole]) then
510 | Exit;
511 |
512 | Result := true;
513 | end;
514 |
515 | procedure TCodeCoverage.MarkFullRepaint(const AFileName: string);
516 | begin
517 | FullRepaint.Add(AFileName);
518 | end;
519 |
520 | procedure TCodeCoverage.MarkModified(const EditView: IOTAEditView);
521 | begin
522 | CodeSyntaxTrees.Remove(EditView.Buffer.FileName);
523 | end;
524 |
525 | function TCodeCoverage.MethodIdByLineNumber(const EditBuffer: IOTAEditBuffer; Line: Integer; out AID: TMethodID):
526 | Boolean;
527 | var
528 | idx: Integer;
529 | Tracker: IOTAEditLineTracker;
530 | begin
531 | Result := false;
532 | if EditBuffer = nil then
533 | Exit;
534 |
535 | Tracker := EditBuffer.GetEditLineTracker;
536 | if Tracker = nil then
537 | Exit;
538 |
539 | idx := Tracker.IndexOfLine(Line);
540 | if idx < 0 then
541 | Exit;
542 |
543 | AID := Tracker.Data[idx];
544 | Result := true;
545 | end;
546 |
547 | procedure TCodeCoverage.ModuleRenamed(const OldName, NewName: string);
548 | var
549 | idx: Integer;
550 | begin
551 | CoveredMethods.RenameFile(OldName, NewName);
552 | CoveredLines.RenameFile(OldName, NewName);
553 | CodeSyntaxTrees.RenameFile(OldName, NewName);
554 | if FullRepaint.Find(OldName, idx) then begin
555 | FullRepaint.Delete(idx);
556 | FullRepaint.Add(NewName);
557 | end;
558 | end;
559 |
560 | procedure TCodeCoverage.RemoveEditLineNotifier(const Tracker: IOTAEditLineTracker);
561 | var
562 | instance: TEditLineNotifier;
563 | begin
564 | instance := FindEditLineNotifier(Tracker);
565 | if instance <> nil then begin
566 | instance.Release;
567 | end;
568 | end;
569 |
570 | procedure TCodeCoverage.RemoveEditor(const Editor: IOTASourceEditor);
571 | var
572 | EditBuffer: IOTAEditBuffer;
573 | begin
574 | if Supports(Editor, IOTAEditBuffer, EditBuffer) then begin
575 | RemoveLineTracker(EditBuffer);
576 | end;
577 | CoveredLines.Remove(Editor.FileName);
578 | CoveredMethods.Remove(Editor.FileName);
579 | CodeSyntaxTrees.Remove(Editor.FileName);
580 | end;
581 |
582 | procedure TCodeCoverage.RemoveLineTracker(const EditBuffer: IOTAEditBuffer);
583 | var
584 | Tracker: IOTAEditLineTracker;
585 | begin
586 | Tracker := EditBuffer.GetEditLineTracker;
587 | RemoveEditLineNotifier(Tracker);
588 | end;
589 |
590 | procedure TCodeCoverage.RemoveLineTracker(const EditBuffer: IOTAEditBuffer; ALineNumber, AID: Integer);
591 | var
592 | idx: Integer;
593 | Tracker: IOTAEditLineTracker;
594 | begin
595 | Tracker := EditBuffer.GetEditLineTracker;
596 | idx := Tracker.IndexOfData(AID);
597 | if idx < 0 then begin
598 | idx := Tracker.IndexOfLine(ALineNumber);
599 | end;
600 | if idx >= 0 then begin
601 | Tracker.Delete(idx);
602 | end;
603 | end;
604 |
605 | procedure TCodeCoverage.RetrieveResults;
606 | begin
607 | if not Active then
608 | Exit;
609 |
610 | CheckCodeCoverage;
611 | ClearAllCodeCoverage;
612 | Active := false;
613 | end;
614 |
615 | function TCodeCoverage.SelectCurrentMethod(const EditBuffer: IOTAEditBuffer; out AFileName, AMethodName: string; out
616 | LineMin, LineMax: Integer): Boolean;
617 | var
618 | code: TCodeSyntaxTree;
619 | begin
620 | Result := false;
621 |
622 | code := FindSyntaxTree(EditBuffer);
623 | if code <> nil then begin
624 | AFileName := code.FileName;
625 | Result := code.FindCurrentMethod(EditBuffer.TopView, AMethodName, LineMin, LineMax);
626 | end;
627 | end;
628 |
629 | function TCodeCoverage.SelectMethod(const AFileName: string; const AMethod: TCoveredMethod;
630 | out LineMin, LineMax: Integer): Boolean;
631 | var
632 | code: TCodeSyntaxTree;
633 | begin
634 | Result := false;
635 |
636 | code := FindSyntaxTree(AFileName);
637 | if code <> nil then begin
638 | Result := code.SelectMethod(AMethod, LineMin, LineMax);
639 | end;
640 | end;
641 |
642 | function TCodeCoverage.SwitchCodeCoverage: Boolean;
643 | var
644 | curLine: TLineNumber;
645 | curMethod: string;
646 | Data: TCoveredMethod;
647 | EditBuffer: IOTAEditBuffer;
648 | FileName: string;
649 | ID: Integer;
650 | LineMax: Integer;
651 | LineMin: Integer;
652 | begin
653 | EditBuffer := OTA.EditorServices.TopBuffer;
654 | Result := SelectCurrentMethod(EditBuffer, FileName, curMethod, LineMin, LineMax);
655 | if Result then begin
656 | if FindMethod(EditBuffer, LineMin, Data) then begin
657 | CoveredMethods.Remove(FileName, Data.ID);
658 | CoveredLines.Remove(FileName, LineMin, LineMax);
659 | RemoveLineTracker(EditBuffer, Data.Line, Data.ID);
660 | FCurrentMethodState := TCoverState.coverable;
661 | end
662 | else begin
663 | curLine := LineMin;
664 | ID := AddLineTracker(EditBuffer, LineMin);
665 | CoveredMethods.Add(FileName, TCoveredMethod.Create(curMethod, curLine, ID));
666 | FCurrentMethodState := TCoverState.covered;
667 | end;
668 | MarkFullRepaint(FileName);
669 | EditBuffer.TopView.Paint;
670 | end
671 | else begin
672 | ShowMessage('No method body found at cursor!');
673 | FCurrentMethodState := TCoverState.noncoverable;
674 | end;
675 | end;
676 |
677 | procedure TCodeCoverage.TrackedLineChanged(const Tracker: IOTAEditLineTracker; OldLine, NewLine, Data: Integer);
678 | var
679 | FileName: string;
680 | ID: TMethodID;
681 | newLineNumber: TLineNumber;
682 | begin
683 | Valid := false;
684 | FileName := Tracker.GetEditBuffer.FileName;
685 | CoveredLines.Remove(FileName);
686 | ID := Data;
687 | newLineNumber := NewLine;
688 | CoveredMethods.ChangeLineNumber(FileName, ID, newLineNumber);
689 | end;
690 |
691 | procedure TCodeCoverage.UpdateCurrentMethodState;
692 | var
693 | curMethod: string;
694 | Data: TCoveredMethod;
695 | EditBuffer: IOTAEditBuffer;
696 | FileName: string;
697 | LineMax: Integer;
698 | LineMin: Integer;
699 | state: TCoverState;
700 | begin
701 | state := TCoverState.noncoverable;
702 | EditBuffer := OTA.EditorServices.TopBuffer;
703 | if SelectCurrentMethod(EditBuffer, FileName, curMethod, LineMin, LineMax) then begin
704 | state := TCoverState.coverable;
705 | if FindMethod(EditBuffer, LineMin, Data) then begin
706 | state := TCoverState.covered;
707 | end;
708 | end;
709 | FCurrentMethodState := state;
710 | end;
711 |
712 | end.
713 |
714 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.KeyBindings.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.KeyBindings;
2 |
3 | interface
4 |
5 | uses
6 | System.Classes,
7 | CodeCoverage.Notifier,
8 | ToolsAPI;
9 |
10 | type
11 | TKeyboardBinding = class(TCodeCoverageNotifier, IOTAKeyboardBinding)
12 | private
13 | FSwitchKeyCode: TShortCut;
14 | procedure SwitchCodeCoverage(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult);
15 | protected
16 | function DoRegister: Integer; override;
17 | procedure DoUnregister(ID: Integer); override;
18 | public
19 | procedure BindKeyboard(const BindingServices: IOTAKeyBindingServices);
20 | function GetBindingType: TBindingType;
21 | function GetDisplayName: string;
22 | function GetName: string;
23 | end;
24 |
25 | implementation
26 |
27 | uses
28 | Winapi.Windows,
29 | CodeCoverage.ApiHelper;
30 |
31 | resourcestring
32 | SCodeCoverageBindings = 'Code Coverage Bindings';
33 |
34 | const
35 | cCodeCoverageBindings = 'CodeCoverageBindings';
36 |
37 | procedure TKeyboardBinding.BindKeyboard(const BindingServices: IOTAKeyBindingServices);
38 | begin
39 | FSwitchKeyCode := scCtrl or scAlt or VK_F5;
40 | BindingServices.AddKeyBinding([FSwitchKeyCode], SwitchCodeCoverage, nil)
41 | end;
42 |
43 | function TKeyboardBinding.DoRegister: Integer;
44 | begin
45 | Result := OTA.KeyboardServices.AddKeyboardBinding(Self);
46 | end;
47 |
48 | procedure TKeyboardBinding.DoUnregister(ID: Integer);
49 | begin
50 | OTA.KeyboardServices.RemoveKeyboardBinding(ID);
51 | inherited;
52 | end;
53 |
54 | function TKeyboardBinding.GetBindingType: TBindingType;
55 | begin
56 | result := btPartial;
57 | end;
58 |
59 | function TKeyboardBinding.GetDisplayName: string;
60 | begin
61 | result := SCodeCoverageBindings;
62 | end;
63 |
64 | function TKeyboardBinding.GetName: string;
65 | begin
66 | result := cCodeCoverageBindings;
67 | end;
68 |
69 | procedure TKeyboardBinding.SwitchCodeCoverage(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult);
70 | begin
71 | CodeCoverage.SwitchCodeCoverage;
72 | BindingResult := krHandled;
73 | end;
74 |
75 | end.
76 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.Main.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.Main;
2 |
3 | interface
4 |
5 | uses
6 | Vcl.Graphics, Vcl.ComCtrls,
7 | CodeCoverage.Handler;
8 |
9 | type
10 | TMagician = class
11 | private
12 | FAboutBitmap: TBitmap;
13 | FCodeCoverage: TCodeCoverage;
14 | FPluginInfoID: Integer;
15 | FSplashBitmap: TBitmap;
16 | FVersion: string;
17 | class var
18 | FInstance: TMagician;
19 | function CreateFromIconResource(ASize: Integer): TBitmap; overload;
20 | function GetAboutBitmap: TBitmap;
21 | function GetDescription: string;
22 | function GetSplashBitmap: TBitmap;
23 | function GetTitle: string;
24 | function GetVersion: string;
25 | protected
26 | procedure AddMenuItems;
27 | procedure AddToolbars;
28 | procedure RemoveMenuItem;
29 | procedure RemoveToolButtons; overload;
30 | procedure RemoveToolButtons(ToolBar: TToolBar); overload;
31 | property AboutBitmap: TBitmap read GetAboutBitmap;
32 | property CodeCoverage: TCodeCoverage read FCodeCoverage;
33 | property Description: string read GetDescription;
34 | property SplashBitmap: TBitmap read GetSplashBitmap;
35 | property Title: string read GetTitle;
36 | property Version: string read GetVersion;
37 | public
38 | constructor Create;
39 | destructor Destroy; override;
40 | class procedure CreateInstance;
41 | class procedure DestroyInstance;
42 | end;
43 |
44 | procedure Register;
45 |
46 | implementation
47 |
48 | uses
49 | System.IOUtils, System.Types, System.StrUtils, System.Classes,
50 | Vcl.Controls, Vcl.Forms,
51 | ToolsApi, CodeCoverage.Tools, CodeCoverage.Consts, CodeCoverage.DM, CodeCoverage.KeyBindings, CodeCoverage.ApiHelper,
52 | CodeCoverage.Images.DM;
53 |
54 | resourcestring
55 | SCodeCoverage = 'Code Coverage';
56 |
57 | const
58 | cRunDebugMenuItemName = 'RunRunNoDebugItem';
59 | cRunMenuItemName = 'RunRunItem';
60 | cSplashBitmapSize = 24;
61 | cAboutBitmapSize = 48;
62 | cCodeCoverageIni = 'CodeCoverage.ini';
63 | cCodeCoverageToolbar = 'CodeCoverageToolbar';
64 |
65 | type
66 | TToolButtonHelper = class helper for TToolButton
67 | public
68 | function GetToolBar: TToolBar;
69 | procedure SetToolBar(const Value: TToolBar);
70 | property ToolBar: TToolBar read GetToolBar write SetToolBar;
71 | end;
72 |
73 | function TToolButtonHelper.GetToolBar: TToolBar;
74 | begin
75 | Result := FToolBar;
76 | end;
77 |
78 | procedure TToolButtonHelper.SetToolBar(const Value: TToolBar);
79 | begin
80 | inherited SetToolBar(Value);
81 | end;
82 |
83 | constructor TMagician.Create;
84 | begin
85 | inherited;
86 |
87 | FCodeCoverage := TCodeCoverage.Create;
88 | FCodeCoverage.Initialize;
89 |
90 | dmCodeCoverageImages := TdmCodeCoverageImages.Create(nil);
91 | dmCodeCoverage := TdmCodeCoverage.Create(nil);
92 | dmCodeCoverage.CodeCoverage := CodeCoverage;
93 |
94 | {$IF Declared(TGraphicArray) }
95 | SplashScreenServices.AddPluginBitmap(Title, dmCodeCoverageImages.ImageArray[cIconName], False, '', '');
96 | FPluginInfoID := OTA.AboutBoxServices.AddPluginInfo(Title, Description, dmCodeCoverageImages.ImageArray[cIconName]);
97 | {$ELSE}
98 | SplashScreenServices.AddPluginBitmap(Title, SplashBitmap.Handle);
99 | FPluginInfoID := OTA.AboutBoxServices.AddPluginInfo(Title, Description, AboutBitmap.Handle, False, '', '', otaafDefined);
100 | {$IFEND}
101 | TKeyboardBinding.Create(CodeCoverage);
102 |
103 | dmCodeCoverage.ImageIndexOffset := NTA.Services.AddImages(dmCodeCoverage.ImageList, cInternalName);
104 | AddMenuItems;
105 | AddToolbars;
106 | end;
107 |
108 | destructor TMagician.Destroy;
109 | begin
110 | CodeCoverage.ClearNotifiers;
111 |
112 | RemoveToolButtons;
113 | RemoveMenuItem;
114 | NTA.Services.AddImages(nil, cInternalName);
115 |
116 | if FPluginInfoID > 0 then begin
117 | OTA.AboutBoxServices.RemovePluginInfo(FPluginInfoID);
118 | end;
119 |
120 | dmCodeCoverage.Free;
121 | dmCodeCoverage := nil;
122 | dmCodeCoverageImages.Free;
123 | dmCodeCoverageImages := nil;
124 |
125 | FCodeCoverage.Free;
126 | FAboutBitmap.Free;
127 | FSplashBitmap.Free;
128 | inherited;
129 | end;
130 |
131 | procedure TMagician.AddMenuItems;
132 | begin
133 | CodeCoverage.RunMenuItem := NTA.FindMenuItem(cRunMenuItemName);
134 |
135 | NTA.Services.AddActionMenu('', dmCodeCoverage.actSwitchCodeCoverage, nil, False);
136 | NTA.Services.AddActionMenu(cRunDebugMenuItemName, dmCodeCoverage.actRunCodeCoverage, dmCodeCoverage.mnuRunCodeCoverage, True);
137 | end;
138 |
139 | procedure TMagician.AddToolbars;
140 | const
141 | cBtnSwitch = cCodeCoverageToolbar + 'BtnSwitch';
142 | cBtnRun = cCodeCoverageToolbar + 'BtnRun';
143 | var
144 | btn: TToolButton;
145 | dbgR: TRect;
146 | I: Integer;
147 | tlb: TToolBar;
148 | tlbDebug: TToolBar;
149 | begin
150 | tlb := NTA.Services.ToolBar[cCodeCoverageToolbar];
151 | if tlb <> nil then begin
152 | { remove existent buttons - just in case... }
153 | for I := tlb.ButtonCount - 1 downto 0 do begin
154 | btn := tlb.Buttons[I];
155 | if MatchStr(btn.Name, [cBtnRun, cBtnSwitch]) then begin
156 | btn.ToolBar := nil;
157 | btn.Free;
158 | end;
159 | end;
160 | end
161 | else begin
162 | tlbDebug := NTA.Services.ToolBar[sDebugToolBar];
163 | dbgR := tlbDebug.BoundsRect;
164 | tlb := NTA.Services.NewToolbar(cCodeCoverageToolbar, SCodeCoverage, tlbDebug.Name, False);
165 | tlb.AutoSize := True;
166 | tlb.SetBounds(dbgR.Right + 1, dbgR.Top, tlb.Width, dbgR.Height);
167 | end;
168 | NTA.Services.AddToolButton(tlb.Name, cBtnRun, dmCodeCoverage.actRunCodeCoverage);
169 | NTA.Services.AddToolButton(tlb.Name, cBtnSwitch, dmCodeCoverage.actSwitchCodeCoverage);
170 | tlb.Visible := True;
171 | end;
172 |
173 | function TMagician.CreateFromIconResource(ASize: Integer): TBitmap;
174 | begin
175 | Result := TTools.CreateFromIconResource(cIconName, ASize);
176 | end;
177 |
178 | class procedure TMagician.CreateInstance;
179 | begin
180 | FInstance := TMagician.Create;
181 | end;
182 |
183 | class procedure TMagician.DestroyInstance;
184 | begin
185 | FInstance.Free;
186 | end;
187 |
188 | function TMagician.GetAboutBitmap: TBitmap;
189 | begin
190 | if FAboutBitmap = nil then begin
191 | FAboutBitmap := CreateFromIconResource(cAboutBitmapSize);
192 | end;
193 | result := FAboutBitmap;
194 | end;
195 |
196 | function TMagician.GetDescription: string;
197 | begin
198 | Result := SDescription + sLineBreak + sLineBreak + cCopyRight;
199 | end;
200 |
201 | function TMagician.GetSplashBitmap: TBitmap;
202 | begin
203 | if FSplashBitmap = nil then begin
204 | FSplashBitmap := CreateFromIconResource(cSplashBitmapSize);
205 | end;
206 | result := FSplashBitmap;
207 | end;
208 |
209 | function TMagician.GetTitle: string;
210 | begin
211 | Result := cTitle + ' ' + Version;
212 | end;
213 |
214 | function TMagician.GetVersion: string;
215 | begin
216 | if FVersion = '' then begin
217 | FVersion := TTools.AppVersion;
218 | end;
219 | Result := FVersion;
220 | end;
221 |
222 | procedure TMagician.RemoveMenuItem;
223 | begin
224 | dmCodeCoverage.RemoveMenuItems;
225 | dmCodeCoverage.RemoveActions;
226 | end;
227 |
228 | procedure TMagician.RemoveToolButtons;
229 | const
230 | { IDE's Toolbar names }
231 | cToolBarNames: array of string = [sCustomToolBar, sStandardToolBar, sDebugToolBar, sViewToolBar, sDesktopToolBar,
232 | sInternetToolBar, sCORBAToolBar, sAlignToolbar, sBrowserToolbar, sHTMLDesignToolbar, sHTMLFormatToolbar,
233 | sHTMLTableToolbar, sPersonalityToolBar, sPositionToolbar, sSpacingToolbar, sIDEInsightToolbar,
234 | sPlatformDeviceToolbar];
235 | var
236 | S: string;
237 | tlb: TToolBar;
238 | begin
239 | tlb := NTA.Services.ToolBar[cCodeCoverageToolbar];
240 | if tlb <> nil then begin
241 | RemoveToolButtons(tlb);
242 | tlb.Visible := (tlb.ButtonCount > 0);
243 | end;
244 |
245 | for S in cToolBarNames do begin
246 | RemoveToolButtons(NTA.Services.ToolBar[S]);
247 | end;
248 | end;
249 |
250 | procedure TMagician.RemoveToolButtons(ToolBar: TToolBar);
251 | var
252 | I: Integer;
253 | Btn: TToolButton;
254 | begin
255 | if ToolBar = nil then Exit;
256 |
257 | for I := ToolBar.ButtonCount - 1 downto 0 do begin
258 | Btn := ToolBar.Buttons[I];
259 | { someone could have just created a new button from our actions in the customize dialog }
260 | if dmCodeCoverage.IsMyAction(Btn.Action) then begin
261 | Btn.ToolBar := nil;
262 | Btn.Free;
263 | end;
264 | end;
265 | end;
266 |
267 | procedure Register;
268 | begin
269 | TMagician.CreateInstance;
270 | end;
271 |
272 | initialization
273 | finalization
274 | TMagician.DestroyInstance;
275 | end.
276 |
277 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.Notifier.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.Notifier;
2 |
3 | interface
4 |
5 | uses
6 | System.Types, System.Classes,
7 | Vcl.Graphics,
8 | CodeCoverage.ApiHelper, CodeCoverage.Types,
9 | ToolsAPI, DockForm;
10 |
11 | type
12 | ICodeCoverage = interface
13 | ['{BCF35C6D-C396-4A3E-8237-D5B99328D72D}']
14 | function CheckFullRepaint(const EditView: IOTAEditView): Boolean;
15 | procedure CreateEditViewNotifier(const View: IOTAEditView);
16 | procedure DrawImage(ACanvas: TCanvas; X, Y, Index: Integer);
17 | procedure EnableCodeCoverage(const AProcess: IOTAProcess);
18 | function FindCoveredLinesList(const EditView: IOTAEditView): TCoveredLinesList;
19 | function FindCoveredMethodList(const EditView: IOTAEditView): TCoveredMethodList;
20 | function GetImageIndexCodeCoverage: Integer;
21 | function GetImageIndexNoCoverage: Integer;
22 | function GetValid: Boolean;
23 | procedure MarkModified(const EditView: IOTAEditView);
24 | function MethodIdByLineNumber(const EditBuffer: IOTAEditBuffer; Line: Integer; out AID: TMethodID): Boolean;
25 | procedure ModuleRenamed(const OldName, NewName: string);
26 | procedure RemoveEditor(const Editor: IOTASourceEditor);
27 | procedure RetrieveResults;
28 | function SwitchCodeCoverage: Boolean;
29 | procedure TrackedLineChanged(const Tracker: IOTAEditLineTracker; OldLine, NewLine, Data: Integer);
30 | procedure UpdateCurrentMethodState;
31 | property ImageIndexCodeCoverage: Integer read GetImageIndexCodeCoverage;
32 | property ImageIndexNoCoverage: Integer read GetImageIndexNoCoverage;
33 | property Valid: Boolean read GetValid;
34 | end;
35 |
36 | type
37 | TCodeCoverageNotifier = class(THostedNotifier)
38 | private
39 | FCodeCoverage: ICodeCoverage;
40 | protected
41 | procedure CheckNotifierHost(ANotifierHost: TNotifierHost); override;
42 | property CodeCoverage: ICodeCoverage read FCodeCoverage;
43 | public
44 | constructor Create(ANotifierHost: TNotifierHost);
45 | end;
46 |
47 | TCodeCoverageNotifier = class(TCodeCoverageNotifier)
48 | private
49 | FTarget: T;
50 | public
51 | constructor Create(AParentInstance: TNotifierHost; const ATarget: T);
52 | function HandlesTarget(const ATarget: T): Boolean; virtual; abstract;
53 | property Target: T read FTarget write FTarget;
54 | end;
55 |
56 | type
57 | TDebuggerNotifier = class(TCodeCoverageNotifier, IOTADebuggerNotifier, IOTADebuggerNotifier90)
58 | protected
59 | function DoRegister: Integer; override;
60 | procedure DoUnregister(ID: Integer); override;
61 | public
62 | function BeforeProgramLaunch(const Project: IOTAProject): Boolean;
63 | procedure BreakpointAdded(const Breakpoint: IOTABreakpoint);
64 | procedure BreakpointChanged(const Breakpoint: IOTABreakpoint);
65 | procedure BreakpointDeleted(const Breakpoint: IOTABreakpoint);
66 | procedure CurrentProcessChanged(const Process: IOTAProcess);
67 | procedure ProcessCreated(const Process: IOTAProcess);
68 | procedure ProcessDestroyed(const Process: IOTAProcess);
69 | procedure ProcessMemoryChanged;
70 | procedure ProcessStateChanged(const Process: IOTAProcess);
71 | end;
72 |
73 | type
74 | TEditLineNotifier = class(TCodeCoverageNotifier, IOTAEditLineNotifier)
75 | private
76 | FNextID: Integer;
77 | protected
78 | function DoRegister: Integer; override;
79 | procedure DoUnregister(ID: Integer); override;
80 | procedure InternalDestroyed; override;
81 | public
82 | function HandlesTarget(const ATarget: IOTAEditLineTracker): Boolean; override;
83 | procedure LineChanged(OldLine: Integer; NewLine: Integer; Data: Integer);
84 | function NextID: Integer;
85 | end;
86 |
87 | type
88 | TEditorNotifier = class(TCodeCoverageNotifier, IOTAEditorNotifier)
89 | private
90 | protected
91 | function DoRegister: Integer; override;
92 | procedure DoUnregister(ID: Integer); override;
93 | procedure InternalDestroyed; override;
94 | public
95 | function HandlesTarget(const ATarget: IOTASourceEditor): Boolean; override;
96 | procedure ViewActivated(const View: IOTAEditView);
97 | procedure ViewNotification(const View: IOTAEditView; Operation: TOperation);
98 | end;
99 |
100 | type
101 | TEditServicesNotifier = class(TCodeCoverageNotifier, INTAEditServicesNotifier)
102 | protected
103 | function DoRegister: Integer; override;
104 | procedure DoUnregister(ID: Integer); override;
105 | public
106 | procedure DockFormRefresh(const EditWindow: INTAEditWindow; DockForm: TDockableForm);
107 | procedure DockFormUpdated(const EditWindow: INTAEditWindow; DockForm: TDockableForm);
108 | procedure DockFormVisibleChanged(const EditWindow: INTAEditWindow; DockForm: TDockableForm);
109 | procedure EditorViewActivated(const EditWindow: INTAEditWindow; const EditView: IOTAEditView);
110 | procedure EditorViewModified(const EditWindow: INTAEditWindow; const EditView: IOTAEditView);
111 | procedure WindowActivated(const EditWindow: INTAEditWindow);
112 | procedure WindowCommand(const EditWindow: INTAEditWindow; Command: Integer; Param: Integer; var Handled: Boolean);
113 | procedure WindowNotification(const EditWindow: INTAEditWindow; Operation: TOperation);
114 | procedure WindowShow(const EditWindow: INTAEditWindow; Show: Boolean; LoadedFromDesktop: Boolean);
115 | end;
116 |
117 | type
118 | TEditViewNotifier = class(TCodeCoverageNotifier, INTAEditViewNotifier)
119 | private
120 | FCoveredLinesList: TCoveredLinesList;
121 | FCoveredMethodList: TCoveredMethodList;
122 | protected
123 | function DoRegister: Integer; override;
124 | procedure DoUnregister(ID: Integer); override;
125 | public
126 | procedure BeginPaint(const View: IOTAEditView; var FullRepaint: Boolean);
127 | procedure EditorIdle(const View: IOTAEditView);
128 | procedure EndPaint(const View: IOTAEditView);
129 | function HandlesTarget(const ATarget: IOTAEditView): Boolean; override;
130 | procedure PaintLine(const View: IOTAEditView; LineNumber: Integer; const LineText: PAnsiChar; const TextWidth: Word;
131 | const LineAttributes: TOTAAttributeArray; const Canvas: TCanvas; const TextRect: TRect; const LineRect: TRect;
132 | const CellSize: TSize);
133 | property CoveredLinesList: TCoveredLinesList read FCoveredLinesList;
134 | property CoveredMethodList: TCoveredMethodList read FCoveredMethodList;
135 | end;
136 |
137 | type
138 | TModuleNotifier = class(TCodeCoverageNotifier, IOTAModuleNotifier)
139 | private
140 | FFileName: string;
141 | protected
142 | function DoRegister: Integer; override;
143 | procedure DoUnregister(ID: Integer); override;
144 | public
145 | function CheckOverwrite: Boolean;
146 | function HandlesTarget(const ATarget: IOTAModule): Boolean; override;
147 | procedure ModuleRenamed(const NewName: string);
148 | property FileName: string read FFileName write FFileName;
149 | end;
150 |
151 | implementation
152 |
153 | uses
154 | System.SysUtils, System.StrUtils, System.Math;
155 |
156 | type
157 | TCanvasState = class
158 | private
159 | FBrush: TBrushRecall;
160 | FFont: TFontRecall;
161 | FPen: TPenRecall;
162 | public
163 | constructor Create(ACanvas: TCanvas);
164 | destructor Destroy; override;
165 | end;
166 |
167 | procedure TCodeCoverageNotifier.CheckNotifierHost(ANotifierHost: TNotifierHost);
168 | begin
169 | inherited;
170 | if not Supports(ANotifierHost, ICodeCoverage) then
171 | raise EProgrammerNotFound.Create('NotifierHost must support ICodeCoverage!');
172 | end;
173 |
174 | constructor TCodeCoverageNotifier.Create(ANotifierHost: TNotifierHost);
175 | begin
176 | inherited Create(ANotifierHost);
177 | { We know that this will succeed }
178 | FCodeCoverage := NotifierHost as ICodeCoverage;
179 | end;
180 |
181 | function TDebuggerNotifier.BeforeProgramLaunch(const Project: IOTAProject): Boolean;
182 | begin
183 | Result := True;
184 | end;
185 |
186 | procedure TDebuggerNotifier.BreakpointAdded(const Breakpoint: IOTABreakpoint);
187 | begin
188 | end;
189 |
190 | procedure TDebuggerNotifier.BreakpointChanged(const Breakpoint: IOTABreakpoint);
191 | begin
192 | end;
193 |
194 | procedure TDebuggerNotifier.BreakpointDeleted(const Breakpoint: IOTABreakpoint);
195 | begin
196 | end;
197 |
198 | procedure TDebuggerNotifier.CurrentProcessChanged(const Process: IOTAProcess);
199 | begin
200 | end;
201 |
202 | function TDebuggerNotifier.DoRegister: Integer;
203 | begin
204 | Result := OTA.DebuggerServices.AddNotifier(Self);
205 | end;
206 |
207 | procedure TDebuggerNotifier.DoUnregister(ID: Integer);
208 | begin
209 | OTA.DebuggerServices.RemoveNotifier(ID);
210 | inherited;
211 | end;
212 |
213 | procedure TDebuggerNotifier.ProcessCreated(const Process: IOTAProcess);
214 | begin
215 | end;
216 |
217 | procedure TDebuggerNotifier.ProcessDestroyed(const Process: IOTAProcess);
218 | begin
219 | end;
220 |
221 | procedure TDebuggerNotifier.ProcessMemoryChanged;
222 | begin
223 | end;
224 |
225 | procedure TDebuggerNotifier.ProcessStateChanged(const Process: IOTAProcess);
226 | begin
227 | case Process.ProcessState of
228 | psNothing: ;
229 | psRunning: CodeCoverage.EnableCodeCoverage(Process);
230 | psStopping: ;
231 | psStopped: ;
232 | psFault: ;
233 | psResFault: ;
234 | psTerminated: CodeCoverage.RetrieveResults;
235 | psException: ;
236 | psNoProcess: ;
237 | end;
238 | end;
239 |
240 | function TEditLineNotifier.DoRegister: Integer;
241 | begin
242 | Result := inherited DoRegister;
243 | if Target <> nil then begin
244 | Result := Target.AddNotifier(Self);
245 | end;
246 | end;
247 |
248 | procedure TEditLineNotifier.DoUnregister(ID: Integer);
249 | begin
250 | if Target <> nil then begin
251 | Target.RemoveNotifier(ID);
252 | end;
253 | inherited;
254 | end;
255 |
256 | function TEditLineNotifier.HandlesTarget(const ATarget: IOTAEditLineTracker): Boolean;
257 | begin
258 | Result := (ATarget = Target);
259 | end;
260 |
261 | procedure TEditLineNotifier.InternalDestroyed;
262 | var
263 | I: Integer;
264 | begin
265 | for I := Target.Count - 1 downto 0 do begin
266 | Target.Delete(I);
267 | end;
268 | inherited;
269 | end;
270 |
271 | procedure TEditLineNotifier.LineChanged(OldLine, NewLine, Data: Integer);
272 | begin
273 | CodeCoverage.TrackedLineChanged(Target, OldLine, NewLine, Data);
274 | end;
275 |
276 | function TEditLineNotifier.NextID: Integer;
277 | begin
278 | if Target.Count = 0 then begin
279 | FNextID := 0;
280 | end;
281 | Result := FNextID;
282 | Inc(FNextID);
283 | end;
284 |
285 | function TEditorNotifier.DoRegister: Integer;
286 | begin
287 | Result := inherited DoRegister;
288 | if Target <> nil then begin
289 | Result := Target.AddNotifier(Self);
290 | end;
291 | end;
292 |
293 | procedure TEditorNotifier.DoUnregister(ID: Integer);
294 | begin
295 | if Target <> nil then begin
296 | Target.RemoveNotifier(ID);
297 | end;
298 | inherited;
299 | end;
300 |
301 | function TEditorNotifier.HandlesTarget(const ATarget: IOTASourceEditor): Boolean;
302 | begin
303 | Result := (ATarget.FileName = Target.FileName);
304 | end;
305 |
306 | procedure TEditorNotifier.InternalDestroyed;
307 | begin
308 | if Target <> nil then begin
309 | CodeCoverage.RemoveEditor(Target);
310 | end;
311 | inherited;
312 | end;
313 |
314 | procedure TEditorNotifier.ViewActivated(const View: IOTAEditView);
315 | begin
316 | end;
317 |
318 | procedure TEditorNotifier.ViewNotification(const View: IOTAEditView; Operation: TOperation);
319 | begin
320 | case Operation of
321 | opInsert: CodeCoverage.CreateEditViewNotifier(View);
322 | opRemove: ;
323 | end;
324 | end;
325 |
326 | procedure TEditServicesNotifier.DockFormRefresh(const EditWindow: INTAEditWindow; DockForm: TDockableForm);
327 | begin
328 | end;
329 |
330 | procedure TEditServicesNotifier.DockFormUpdated(const EditWindow: INTAEditWindow; DockForm: TDockableForm);
331 | begin
332 | end;
333 |
334 | procedure TEditServicesNotifier.DockFormVisibleChanged(const EditWindow: INTAEditWindow; DockForm: TDockableForm);
335 | begin
336 | end;
337 |
338 | function TEditServicesNotifier.DoRegister: Integer;
339 | begin
340 | Result := OTA.EditorServices.AddNotifier(Self);
341 | end;
342 |
343 | procedure TEditServicesNotifier.DoUnregister(ID: Integer);
344 | begin
345 | OTA.EditorServices.RemoveNotifier(ID);
346 | inherited;
347 | end;
348 |
349 | procedure TEditServicesNotifier.EditorViewActivated(const EditWindow: INTAEditWindow; const EditView: IOTAEditView);
350 | begin
351 | CodeCoverage.CreateEditViewNotifier(EditView);
352 | CodeCoverage.UpdateCurrentMethodState;
353 | end;
354 |
355 | procedure TEditServicesNotifier.EditorViewModified(const EditWindow: INTAEditWindow; const EditView: IOTAEditView);
356 | begin
357 | CodeCoverage.MarkModified(EditView);
358 | end;
359 |
360 | procedure TEditServicesNotifier.WindowActivated(const EditWindow: INTAEditWindow);
361 | begin
362 | end;
363 |
364 | procedure TEditServicesNotifier.WindowCommand(const EditWindow: INTAEditWindow; Command, Param: Integer;
365 | var Handled: Boolean);
366 | begin
367 | end;
368 |
369 | procedure TEditServicesNotifier.WindowNotification(const EditWindow: INTAEditWindow; Operation: TOperation);
370 | begin
371 | end;
372 |
373 | procedure TEditServicesNotifier.WindowShow(const EditWindow: INTAEditWindow; Show, LoadedFromDesktop: Boolean);
374 | begin
375 | end;
376 |
377 | procedure TEditViewNotifier.BeginPaint(const View: IOTAEditView; var FullRepaint: Boolean);
378 | begin
379 | if CodeCoverage.CheckFullRepaint(View) then begin
380 | FullRepaint := True;
381 | end;
382 |
383 | FCoveredMethodList := CodeCoverage.FindCoveredMethodList(View);
384 |
385 | if CodeCoverage.Valid then begin
386 | FCoveredLinesList := CodeCoverage.FindCoveredLinesList(View);
387 | end
388 | else begin
389 | FCoveredLinesList := nil;
390 | end;
391 | end;
392 |
393 | function TEditViewNotifier.DoRegister: Integer;
394 | begin
395 | Result := inherited DoRegister;
396 | if Target <> nil then begin
397 | Result := Target.AddNotifier(Self);
398 | end;
399 | end;
400 |
401 | procedure TEditViewNotifier.DoUnregister(ID: Integer);
402 | begin
403 | if Target <> nil then begin
404 | Target.RemoveNotifier(ID);
405 | Target := nil;
406 | end;
407 | inherited;
408 | end;
409 |
410 | procedure TEditViewNotifier.EditorIdle(const View: IOTAEditView);
411 | begin
412 | if View.SameView(OTA.EditorServices.TopView) then begin
413 | CodeCoverage.UpdateCurrentMethodState;
414 | end;
415 | end;
416 |
417 | procedure TEditViewNotifier.EndPaint(const View: IOTAEditView);
418 | begin
419 | FCoveredMethodList := nil;
420 | FCoveredLinesList := nil;
421 | end;
422 |
423 | function TEditViewNotifier.HandlesTarget(const ATarget: IOTAEditView): Boolean;
424 | begin
425 | Result := ATarget.SameView(Target);
426 | end;
427 |
428 | procedure TEditViewNotifier.PaintLine(const View: IOTAEditView; LineNumber: Integer; const LineText: PAnsiChar;
429 | const TextWidth: Word; const LineAttributes: TOTAAttributeArray; const Canvas: TCanvas; const TextRect,
430 | LineRect: TRect; const CellSize: TSize);
431 | var
432 | coveredMethod: TCoveredMethod;
433 | gutterPos: TPoint;
434 | PassCount: THitCount;
435 | S: string;
436 | canvasState: TCanvasState;
437 | afterText: TPoint;
438 | beforeText: TPoint;
439 | curID: TMethodID;
440 | begin
441 | canvasState := TCanvasState.Create(Canvas);
442 | try
443 | gutterPos := TPoint.Create(LineRect.TopLeft);
444 | afterText := TPoint.Create(TextRect.Right + CellSize.cx, LineRect.Top);
445 | beforeText := TPoint.Create(TextRect.Left, LineRect.Top);
446 |
447 | if CoveredMethodList <> nil then begin
448 | if CodeCoverage.MethodIdByLineNumber(View.Buffer, LineNumber, curID) then begin
449 | if CoveredMethodList.Find(curID, coveredMethod) then begin
450 | CodeCoverage.DrawImage(Canvas, gutterPos.X, gutterPos.Y, CodeCoverage.ImageIndexCodeCoverage);
451 | if CodeCoverage.Valid then begin
452 | canvas.Brush.Style := bsClear;
453 | canvas.Font.Color := clLtGray;
454 | Canvas.TextOut(afterText.X, afterText.Y, Format('[%d%%]', [coveredMethod.Percent]));
455 | end;
456 | end;
457 | end;
458 | end;
459 |
460 | if CoveredLinesList <> nil then begin
461 | if CoveredLinesList.TryGetValue(LineNumber, PassCount) then begin
462 | S := IfThen(PassCount < 0, '○', '●');
463 | canvas.Brush.Style := bsClear;
464 | canvas.Font.Color := IfThen(PassCount = 0, clRed, clBlue);
465 | Canvas.TextOut(beforeText.X, beforeText.Y, S);
466 | if PassCount = 0 then begin
467 | CodeCoverage.DrawImage(Canvas, afterText.X, afterText.Y, CodeCoverage.ImageIndexNoCoverage);
468 | end
469 | else if PassCount > 0 then begin
470 | canvas.Font.Color := clLtGray;
471 | Canvas.TextOut(afterText.X, afterText.Y, Format('[%d]', [PassCount]));
472 | end;
473 | end;
474 | end;
475 |
476 | finally
477 | canvasState.Free;
478 | end;
479 | end;
480 |
481 | function TModuleNotifier.DoRegister: Integer;
482 | begin
483 | Result := inherited DoRegister;
484 | if Target <> nil then begin
485 | Result := Target.AddNotifier(Self);
486 | FileName := Target.FileName;
487 | end;
488 | end;
489 |
490 | procedure TModuleNotifier.DoUnregister(ID: Integer);
491 | begin
492 | if Target <> nil then begin
493 | Target.RemoveNotifier(ID);
494 | Target := nil;
495 | end;
496 | inherited;
497 | end;
498 |
499 | function TModuleNotifier.CheckOverwrite: Boolean;
500 | begin
501 | Result := True;
502 | end;
503 |
504 | function TModuleNotifier.HandlesTarget(const ATarget: IOTAModule): Boolean;
505 | begin
506 | Result := (ATarget = Target);
507 | end;
508 |
509 | procedure TModuleNotifier.ModuleRenamed(const NewName: string);
510 | begin
511 | if FileName <> NewName then begin
512 | CodeCoverage.ModuleRenamed(FileName, NewName);
513 | FileName := NewName;
514 | end;
515 | end;
516 |
517 | constructor TCodeCoverageNotifier.Create(AParentInstance: TNotifierHost; const ATarget: T);
518 | begin
519 | inherited Create(AParentInstance);
520 | FTarget := ATarget;
521 | end;
522 |
523 | constructor TCanvasState.Create(ACanvas: TCanvas);
524 | begin
525 | inherited Create;
526 | FPen := TPenRecall.Create(ACanvas.Pen);
527 | FFont := TFontRecall.Create(ACanvas.Font);
528 | FBrush := TBrushRecall.Create(ACanvas.Brush);
529 | end;
530 |
531 | destructor TCanvasState.Destroy;
532 | begin
533 | FBrush.Free;
534 | FFont.Free;
535 | FPen.Free;
536 | inherited;
537 | end;
538 |
539 | end.
540 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.SyntaxTypes.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.SyntaxTypes;
2 |
3 | interface
4 |
5 | uses
6 | System.SysUtils,
7 | DelphiAST.Classes,
8 | CodeCoverage.Types,
9 | ToolsAPI;
10 |
11 | type
12 | TCodeSyntaxTree = class
13 | private
14 | FFileName: string;
15 | FNodeImplementation: TSyntaxNode;
16 | FSyntaxTree: TSyntaxNode;
17 | procedure InitNodes;
18 | procedure SetSyntaxTree(const Value: TSyntaxNode);
19 | protected
20 | function FindMethod(ALineNumber: Integer): TCompoundSyntaxNode; overload;
21 | function FindMethod(const AMethod: TCoveredMethod): TCompoundSyntaxNode; overload;
22 | function FindMethod(APredicate: TPredicate): TCompoundSyntaxNode; overload;
23 | function SelectMethod(ANode: TCompoundSyntaxNode; out LineMin, LineMax: Integer): Boolean; overload;
24 | property NodeImplementation: TSyntaxNode read FNodeImplementation;
25 | property SyntaxTree: TSyntaxNode read FSyntaxTree write SetSyntaxTree;
26 | public
27 | destructor Destroy; override;
28 | procedure Clear;
29 | function FindCurrentMethod(EditView: IOTAEditView; out AMethodName: string; out LineMin, LineMax: Integer): Boolean;
30 | function LoadFrom(Editor: IOTASourceEditor): Boolean;
31 | function SelectMethod(ALineNumber: Integer; out LineMin, LineMax: Integer): Boolean; overload;
32 | function SelectMethod(const AMethod: TCoveredMethod; out LineMin, LineMax: Integer): Boolean; overload;
33 | property FileName: string read FFileName;
34 | end;
35 |
36 | TCodeSyntaxTreeDict = class(TFileNameDict);
37 |
38 | TCodeSyntaxTrees = class
39 | private
40 | FData: TCodeSyntaxTreeDict;
41 | public
42 | constructor Create;
43 | destructor Destroy; override;
44 | function Add(Editor: IOTASourceEditor): TCodeSyntaxTree;
45 | function Find(const AFileName: string; out Value: TCodeSyntaxTree): Boolean;
46 | procedure Remove(const AFileName: string);
47 | procedure RenameFile(const OldName, NewName: string);
48 | end;
49 |
50 | implementation
51 |
52 | uses
53 | System.Math, System.Classes, System.Generics.Collections,
54 | DelphiAST.Consts, DelphiAST;
55 |
56 | destructor TCodeSyntaxTree.Destroy;
57 | begin
58 | FSyntaxTree.Free;
59 | inherited Destroy;
60 | end;
61 |
62 | procedure TCodeSyntaxTree.Clear;
63 | begin
64 | FFileName := '';
65 | FNodeImplementation := nil;
66 | FSyntaxTree.Free;
67 | FSyntaxTree := nil;
68 | end;
69 |
70 | function TCodeSyntaxTree.FindCurrentMethod(EditView: IOTAEditView; out AMethodName: string; out LineMin, LineMax:
71 | Integer): Boolean;
72 | var
73 | curPos: TOTAEditPos;
74 | node: TCompoundSyntaxNode;
75 | begin
76 | Result := False;
77 |
78 | curPos := EditView.CursorPos;
79 | node := FindMethod(curPos.Line);
80 | if node <> nil then begin
81 | if (node.Line = curPos.Line) and (curPos.Col < node.Col) then Exit;
82 | if (node.EndLine = curPos.Line) and (node.EndCol <= curPos.Col) then Exit;
83 |
84 | AMethodName := node.GetAttribute(anName);
85 | LineMin := node.Line;
86 | LineMax := node.EndLine;
87 | Result := True;
88 | end;
89 | end;
90 |
91 | function TCodeSyntaxTree.FindMethod(ALineNumber: Integer): TCompoundSyntaxNode;
92 | begin
93 | Result := FindMethod(
94 | function (Arg: TCompoundSyntaxNode): Boolean
95 | begin
96 | Result := InRange(ALineNumber, Arg.Line, Arg.EndLine - 1);
97 | end);
98 | end;
99 |
100 | function TCodeSyntaxTree.FindMethod(const AMethod: TCoveredMethod): TCompoundSyntaxNode;
101 | var
102 | targetLine: Integer;
103 | targetName: string;
104 | begin
105 | targetName := AMethod.Name;
106 | targetLine := AMethod.Line;
107 | Result := FindMethod(
108 | function (Arg: TCompoundSyntaxNode): Boolean
109 | begin
110 | Result := SameText(Arg.GetAttribute(anName), targetName) and (Arg.Line = targetLine);
111 | end);
112 | end;
113 |
114 | function TCodeSyntaxTree.FindMethod(APredicate: TPredicate): TCompoundSyntaxNode;
115 | var
116 | compNode: TCompoundSyntaxNode;
117 | node: TSyntaxNode;
118 | begin
119 | Result := nil;
120 | if NodeImplementation <> nil then begin
121 | for node in NodeImplementation.ChildNodes do begin
122 | if node is TCompoundSyntaxNode then begin
123 | compNode := node as TCompoundSyntaxNode;
124 | if (compNode.Typ = ntMethod) and APredicate(compNode) then begin
125 | Exit(compNode);
126 | end;
127 | end;
128 | end;
129 | end;
130 | end;
131 |
132 | procedure TCodeSyntaxTree.InitNodes;
133 | begin
134 | if SyntaxTree <> nil then begin
135 | FNodeImplementation := SyntaxTree.FindNode(ntImplementation);
136 | end
137 | else begin
138 | FNodeImplementation := nil;
139 | end;
140 | end;
141 |
142 | function TCodeSyntaxTree.LoadFrom(Editor: IOTASourceEditor): Boolean;
143 | const
144 | cBufferSize = 16*1024;
145 | var
146 | buffer: TBytes;
147 | cntRead: Integer;
148 | curPos: Integer;
149 | stream: TStringStream;
150 | builder: TPasSyntaxTreeBuilder;
151 | reader: IOTAEditReader;
152 | begin
153 | Result := False;
154 | Clear;
155 | if Editor = nil then Exit;
156 |
157 | FFileName := Editor.FileName;
158 |
159 | stream := TStringStream.Create('', TEncoding.UTF8);
160 | try
161 | SetLength(buffer, cBufferSize);
162 | curPos := 0;
163 | reader := Editor.CreateReader;
164 | repeat
165 | cntRead := reader.GetText(curPos, PAnsiChar(@buffer[0]), cBufferSize);
166 | if cntRead > 0 then begin
167 | stream.Write(buffer, cntRead);
168 | curPos := curPos + cntRead;
169 | end;
170 | until cntRead < cBufferSize;
171 | reader := nil;
172 |
173 | stream.Position := 0;
174 | builder := TPasSyntaxTreeBuilder.Create;
175 | try
176 | builder.InitDefinesDefinedByCompiler;
177 | try
178 | SyntaxTree := builder.Run(stream);
179 | Result := True;
180 | except
181 | { We can log the exception somehow, f.i. with CodeSite, but we don't show it!
182 | It is just code that cannot be parsed. Returning False is sufficient. }
183 | on E: Exception do begin
184 | // CodeSite.SendException(E);
185 | end;
186 | end;
187 | finally
188 | builder.Free;
189 | end;
190 | finally
191 | stream.Free;
192 | end;
193 | end;
194 |
195 | function TCodeSyntaxTree.SelectMethod(ALineNumber: Integer; out LineMin, LineMax: Integer): Boolean;
196 | begin
197 | Result := SelectMethod(FindMethod(ALineNumber), LineMin, LineMax);
198 | end;
199 |
200 | function TCodeSyntaxTree.SelectMethod(ANode: TCompoundSyntaxNode; out LineMin, LineMax: Integer): Boolean;
201 | var
202 | compNode: TCompoundSyntaxNode;
203 | begin
204 | Result := False;
205 | if ANode <> nil then begin
206 | compNode := ANode.FindNode(ntStatements) as TCompoundSyntaxNode;
207 | if compNode <> nil then begin
208 | LineMin := compNode.Line;
209 | LineMax := compNode.EndLine;
210 | Result := True;
211 | end;
212 | end;
213 | end;
214 |
215 | function TCodeSyntaxTree.SelectMethod(const AMethod: TCoveredMethod; out LineMin, LineMax: Integer): Boolean;
216 | begin
217 | Result := SelectMethod(FindMethod(AMethod), LineMin, LineMax);
218 | end;
219 |
220 | procedure TCodeSyntaxTree.SetSyntaxTree(const Value: TSyntaxNode);
221 | begin
222 | if FSyntaxTree <> Value then
223 | begin
224 | FSyntaxTree.Free;
225 | FSyntaxTree := Value;
226 | InitNodes;
227 | end;
228 | end;
229 |
230 | constructor TCodeSyntaxTrees.Create;
231 | begin
232 | inherited Create;
233 | FData := TCodeSyntaxTreeDict.Create();
234 | end;
235 |
236 | destructor TCodeSyntaxTrees.Destroy;
237 | begin
238 | FData.Free;
239 | inherited Destroy;
240 | end;
241 |
242 | function TCodeSyntaxTrees.Add(Editor: IOTASourceEditor): TCodeSyntaxTree;
243 | var
244 | tree: TCodeSyntaxTree;
245 | begin
246 | if not FData.TryGetValue(Editor.FileName, Result) then begin
247 | Result := nil;
248 | tree := TCodeSyntaxTree.Create;
249 | try
250 | if tree.LoadFrom(Editor) then begin
251 | Result := tree;
252 | tree := nil;
253 | FData.Add(Editor.FileName, Result);
254 | end
255 | finally
256 | tree.Free;
257 | end;
258 | end;
259 | end;
260 |
261 | function TCodeSyntaxTrees.Find(const AFileName: string; out Value: TCodeSyntaxTree): Boolean;
262 | begin
263 | Result := FData.TryGetValue(AFileName, Value);
264 | end;
265 |
266 | procedure TCodeSyntaxTrees.Remove(const AFileName: string);
267 | begin
268 | FData.Remove(AFileName);
269 | end;
270 |
271 | procedure TCodeSyntaxTrees.RenameFile(const OldName, NewName: string);
272 | begin
273 | FData.RenameFile(OldName, NewName);
274 | end;
275 |
276 | end.
277 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.Tools.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.Tools;
2 |
3 | interface
4 |
5 | { assure Winapi.Windows appears before Vcl.Graphics, because both declare TBitmap }
6 | uses
7 | Winapi.Windows,
8 | System.Classes,
9 | Vcl.Graphics;
10 |
11 | type
12 | TTools = class
13 | private
14 | class var
15 | FVersion: string;
16 | class function GetDescription: string; static;
17 | class function GetTitle: string; static;
18 | class function GetVersion: string; static;
19 | public
20 | class function AppVersion: string; static;
21 | class function CreateFromIconResource(const AName: string; ASize: Integer): TBitmap; overload;
22 | class procedure LoadFromIconResource(Target: TIcon; const AName: string);
23 | class procedure Postpone(AProc: TThreadMethod; ADelayMS: Cardinal = 0); overload;
24 | class procedure Postpone(AProc: TThreadProcedure; ADelayMS: Cardinal = 0); overload;
25 | class property Description: string read GetDescription;
26 | class property Title: string read GetTitle;
27 | class property Version: string read GetVersion;
28 | end;
29 |
30 | implementation
31 |
32 | uses
33 | System.SysUtils, System.Threading,
34 | CodeCoverage.Consts;
35 |
36 | class function TTools.AppVersion: string;
37 | var
38 | build: Cardinal;
39 | major: Cardinal;
40 | minor: Cardinal;
41 | begin
42 | if GetProductVersion(GetModuleName(HInstance), major, minor, build) then begin
43 | Result := Format('V%d.%d.%d', [major, minor, build]); // do not localize
44 | end
45 | else begin
46 | Result := cVersion;
47 | end;
48 | end;
49 |
50 | class function TTools.CreateFromIconResource(const AName: string; ASize: Integer): TBitmap;
51 | var
52 | icon: TIcon;
53 | begin
54 | Result := TBitmap.Create;
55 | icon := TIcon.Create;
56 | try
57 | icon.SetSize(ASize, ASize);
58 | LoadFromIconResource(icon, AName);
59 | Result.Assign(icon);
60 | finally
61 | icon.Free;
62 | end;
63 | end;
64 |
65 | class function TTools.GetDescription: string;
66 | begin
67 | Result := SDescription + sLineBreak + sLineBreak + cCopyRight;
68 | end;
69 |
70 | class function TTools.GetTitle: string;
71 | begin
72 | Result := cTitle + ' ' + Version;
73 | end;
74 |
75 | class function TTools.GetVersion: string;
76 | begin
77 | if FVersion = '' then begin
78 | FVersion := TTools.AppVersion;
79 | end;
80 | Result := FVersion;
81 | end;
82 |
83 | class procedure TTools.LoadFromIconResource(Target: TIcon; const AName: string);
84 | begin
85 | Target.Handle := LoadImage(HInstance, PChar(AName), IMAGE_ICON, Target.Width, Target.Height, 0);
86 | end;
87 |
88 | class procedure TTools.Postpone(AProc: TThreadMethod; ADelayMS: Cardinal = 0);
89 | begin
90 | TTask.Run(
91 | procedure
92 | begin
93 | if ADelayMS > 0 then begin
94 | Sleep(ADelayMS);
95 | end;
96 | TThread.Queue(nil, AProc);
97 | end);
98 | end;
99 |
100 | class procedure TTools.Postpone(AProc: TThreadProcedure; ADelayMS: Cardinal = 0);
101 | begin
102 | TTask.Run(
103 | procedure
104 | begin
105 | if ADelayMS > 0 then begin
106 | Sleep(ADelayMS);
107 | end;
108 | TThread.Queue(nil, AProc);
109 | end);
110 | end;
111 |
112 | end.
113 |
114 |
--------------------------------------------------------------------------------
/Source/CodeCoverage.Types.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.Types;
2 |
3 | interface
4 |
5 | uses
6 | System.Generics.Collections;
7 |
8 | type
9 | { Aliases to clarify the meaning of the type (especially with dictionaries) }
10 | TLineNumber = Integer;
11 | TMethodID = Integer;
12 | THitCount = Integer;
13 | TPercent = Integer;
14 | TFileName = string;
15 |
16 | type
17 | TFileNameDict = class(TObjectDictionary)
18 | public
19 | constructor Create;
20 | procedure RenameFile(const OldName, NewName: string);
21 | end;
22 |
23 | type
24 | TCoveredLinesHandler = reference to procedure(const AFileName: string; ALineNumber, APassCount: Integer);
25 |
26 | TCoveredLinesList = class(TDictionary);
27 |
28 | TCoveredLinesDict = class(TFileNameDict);
29 |
30 | TCoveredLines = class
31 | private
32 | FData: TCoveredLinesDict;
33 | function FindOrAddList(const AFileName: string): TCoveredLinesList;
34 | public
35 | constructor Create;
36 | destructor Destroy; override;
37 | procedure Add(const AFileName: string; const ALineNumber: TLineNumber; const ACount: THitCount);
38 | procedure Clear;
39 | function Find(const AFileName: string): TCoveredLinesList; overload;
40 | function Find(const AFileName: string; const ALineNumber: TLineNumber): THitCount; overload;
41 | procedure Initialize(const AFileName: string; const ALineNumber: TLineNumber; const ACount: THitCount);
42 | function IsEmpty: Boolean;
43 | procedure Iterate(Callback: TCoveredLinesHandler);
44 | procedure Remove(const AFileName: string; const LineMin, LineMax: TLineNumber); overload;
45 | procedure Remove(const AFileName: string); overload;
46 | procedure RenameFile(const OldName, NewName: string);
47 | end;
48 |
49 | type
50 | TCoveredMethod = record
51 | private
52 | FID: TMethodID;
53 | FLine: TLineNumber;
54 | FLineMax: Integer;
55 | FLineMin: Integer;
56 | FName: string;
57 | FPercent: TPercent;
58 | public
59 | constructor Create(const AName: string; const ALine: TLineNumber; const AID: TMethodID);
60 | property ID: TMethodID read FID;
61 | property Line: TLineNumber read FLine write FLine;
62 | property LineMax: Integer read FLineMax write FLineMax;
63 | property LineMin: Integer read FLineMin write FLineMin;
64 | property Name: string read FName write FName;
65 | property Percent: TPercent read FPercent write FPercent;
66 | end;
67 |
68 | TCoveredMethodHandler = reference to procedure(const AFileName: string; const Data: TCoveredMethod);
69 |
70 | TCoveredMethodIndex = class(TDictionary);
71 |
72 | TCoveredMethodList = class
73 | private
74 | FData: TCoveredMethodIndex;
75 | public
76 | constructor Create;
77 | destructor Destroy; override;
78 | procedure Add(const Value: TCoveredMethod);
79 | procedure ChangeLineNumber(const AID: TMethodID; const ALineNumber: TLineNumber); overload;
80 | function Find(const AID: TMethodID; out Value: TCoveredMethod): Boolean;
81 | function IsEmpty: Boolean;
82 | procedure Iterate(const AFileName: string; Callback: TCoveredMethodHandler);
83 | procedure Remove(const AID: TMethodID); overload;
84 | procedure Update(const AID: TMethodID; ALineMin, ALineMax: Integer); overload;
85 | procedure UpdatePercent(const AID: TMethodID; const APercent: TPercent); overload;
86 | end;
87 |
88 | TCoveredMethodDict = class(TFileNameDict);
89 |
90 | TCoveredMethods = class
91 | strict private
92 | function FindOrAddList(const AFileName: string): TCoveredMethodList;
93 | private
94 | FData: TCoveredMethodDict;
95 | public
96 | constructor Create;
97 | destructor Destroy; override;
98 | procedure Add(const AFileName: string; const Data: TCoveredMethod);
99 | procedure ChangeLineNumber(const AFileName: string; const AID: TMethodID; const ALineNumber: TLineNumber);
100 | function Find(const AFileName: string; const AID: TMethodID; out Value: TCoveredMethod): Boolean; overload;
101 | function Find(const AFileName: string): TCoveredMethodList; overload;
102 | function IsEmpty: Boolean;
103 | procedure Iterate(Callback: TCoveredMethodHandler);
104 | procedure Remove(const AFileName: string; const AID: TMethodID); overload;
105 | procedure Remove(const AFileName: string); overload;
106 | procedure RenameFile(const OldName, NewName: string);
107 | procedure Update(const AFileName: string; const AID: TMethodID; ALineMin, ALineMax: Integer); overload;
108 | procedure UpdatePercent(const AFileName: string; const AID: TMethodID; APercent: Integer); overload;
109 | end;
110 |
111 | implementation
112 |
113 | constructor TCoveredMethod.Create(const AName: string; const ALine: TLineNumber; const AID: TMethodID);
114 | begin
115 | FName := AName;
116 | FLine := ALine;
117 | FID := AID;
118 | end;
119 |
120 | constructor TCoveredLines.Create;
121 | begin
122 | inherited Create;
123 | FData := TCoveredLinesDict.Create;
124 | end;
125 |
126 | destructor TCoveredLines.Destroy;
127 | begin
128 | FData.Free;
129 | inherited Destroy;
130 | end;
131 |
132 | procedure TCoveredLines.Add(const AFileName: string; const ALineNumber: TLineNumber; const ACount: THitCount);
133 | var
134 | Count: THitCount;
135 | list: TCoveredLinesList;
136 | begin
137 | list := FindOrAddList(AFileName);
138 | if not list.TryGetValue(ALineNumber, Count) then begin
139 | Count := ACount;
140 | end
141 | else begin
142 | Count := Count + ACount;
143 | end;
144 | list.AddOrSetValue(ALineNumber, Count);
145 | end;
146 |
147 | procedure TCoveredLines.Clear;
148 | begin
149 | FData.Clear;
150 | end;
151 |
152 | function TCoveredLines.Find(const AFileName: string): TCoveredLinesList;
153 | begin
154 | if not FData.TryGetValue(AFileName, Result) then begin
155 | Result := nil;
156 | end;
157 | end;
158 |
159 | function TCoveredLines.Find(const AFileName: string; const ALineNumber: TLineNumber): THitCount;
160 | var
161 | count: THitCount;
162 | list: TCoveredLinesList;
163 | begin
164 | Result := -1;
165 | list := FindOrAddList(AFileName);
166 | if list <> nil then begin
167 | if list.TryGetValue(ALineNumber, count) then begin
168 | Result := count;
169 | end;
170 | end;
171 | end;
172 |
173 | function TCoveredLines.FindOrAddList(const AFileName: string): TCoveredLinesList;
174 | begin
175 | if not FData.TryGetValue(AFileName, Result) then begin
176 | Result := TCoveredLinesList.Create;
177 | FData.Add(AFileName, Result);
178 | end;
179 | end;
180 |
181 | procedure TCoveredLines.Initialize(const AFileName: string; const ALineNumber: TLineNumber; const ACount: THitCount);
182 | var
183 | list: TCoveredLinesList;
184 | begin
185 | list := FindOrAddList(AFileName);
186 | list.AddOrSetValue(ALineNumber, ACount);
187 | end;
188 |
189 | function TCoveredLines.IsEmpty: Boolean;
190 | begin
191 | Result := (FData.Count = 0);
192 | end;
193 |
194 | procedure TCoveredLines.Iterate(Callback: TCoveredLinesHandler);
195 | var
196 | arr: TArray;
197 | line: TLineNumber;
198 | passCount: TCoveredLinesList;
199 | pair: TPair;
200 | begin
201 | for pair in FData do begin
202 | passCount := pair.Value;
203 | arr := passCount.Keys.ToArray;
204 | TArray.Sort(arr);
205 | for line in arr do begin
206 | Callback(pair.Key, line, passCount[line]);
207 | end;
208 | end;
209 | end;
210 |
211 | procedure TCoveredLines.Remove(const AFileName: string; const LineMin, LineMax: TLineNumber);
212 | var
213 | list: TCoveredLinesList;
214 | I: TLineNumber;
215 | begin
216 | list := Find(AFileName);
217 | if list <> nil then begin
218 | for I := LineMin to LineMax do begin
219 | list.Remove(I);
220 | end;
221 | if list.Count = 0 then begin
222 | FData.Remove(AFileName);
223 | end;
224 | end;
225 | end;
226 |
227 | procedure TCoveredLines.Remove(const AFileName: string);
228 | begin
229 | FData.Remove(AFileName);
230 | end;
231 |
232 | procedure TCoveredLines.RenameFile(const OldName, NewName: string);
233 | begin
234 | FData.RenameFile(OldName, NewName);
235 | end;
236 |
237 | constructor TCoveredMethodList.Create;
238 | begin
239 | inherited Create;
240 | FData := TCoveredMethodIndex.Create;
241 | end;
242 |
243 | destructor TCoveredMethodList.Destroy;
244 | begin
245 | FData.Free;
246 | inherited Destroy;
247 | end;
248 |
249 | procedure TCoveredMethodList.Add(const Value: TCoveredMethod);
250 | begin
251 | FData.AddOrSetValue(Value.ID, Value);
252 | end;
253 |
254 | procedure TCoveredMethodList.ChangeLineNumber(const AID: TMethodID; const ALineNumber: TLineNumber);
255 | var
256 | value: TCoveredMethod;
257 | begin
258 | if FData.TryGetValue(AID, value) then begin
259 | value.Line := ALineNumber;
260 | FData.Items[AID] := value;
261 | end;
262 | end;
263 |
264 | function TCoveredMethodList.Find(const AID: TMethodID; out Value: TCoveredMethod): Boolean;
265 | begin
266 | Result := FData.TryGetValue(AID, Value);
267 | end;
268 |
269 | function TCoveredMethodList.IsEmpty: Boolean;
270 | begin
271 | Result := (FData.Count = 0);
272 | end;
273 |
274 | procedure TCoveredMethodList.Iterate(const AFileName: string; Callback: TCoveredMethodHandler);
275 | var
276 | pair: TPair;
277 | begin
278 | for pair in FData do begin
279 | Callback(AFileName, pair.Value);
280 | end;
281 | end;
282 |
283 | procedure TCoveredMethodList.Remove(const AID: TMethodID);
284 | var
285 | data: TCoveredMethod;
286 | begin
287 | if FData.TryGetValue(AID, data) then begin
288 | FData.Remove(AID);
289 | end;
290 | end;
291 |
292 | procedure TCoveredMethodList.Update(const AID: TMethodID; ALineMin, ALineMax: Integer);
293 | var
294 | data: TCoveredMethod;
295 | begin
296 | if FData.TryGetValue(AID, data) then begin
297 | data.LineMin := ALineMin;
298 | data.LineMax := ALineMax;
299 | FData[AID] := data;
300 | end;
301 | end;
302 |
303 | procedure TCoveredMethodList.UpdatePercent(const AID: TMethodID; const APercent: TPercent);
304 | var
305 | data: TCoveredMethod;
306 | begin
307 | if FData.TryGetValue(AID, data) then begin
308 | data.Percent := APercent;
309 | FData[AID] := data;
310 | end;
311 | end;
312 |
313 | constructor TCoveredMethods.Create;
314 | begin
315 | inherited Create;
316 | FData := TCoveredMethodDict.Create;
317 | end;
318 |
319 | destructor TCoveredMethods.Destroy;
320 | begin
321 | FData.Free;
322 | inherited Destroy;
323 | end;
324 |
325 | procedure TCoveredMethods.Add(const AFileName: string; const Data: TCoveredMethod);
326 | var
327 | list: TCoveredMethodList;
328 | begin
329 | list := FindOrAddList(AFileName);
330 | list.Add(Data);
331 | end;
332 |
333 | procedure TCoveredMethods.ChangeLineNumber(const AFileName: string; const AID: TMethodID; const ALineNumber:
334 | TLineNumber);
335 | var
336 | list: TCoveredMethodList;
337 | begin
338 | list := Find(AFileName);
339 | if list <> nil then begin
340 | list.ChangeLineNumber(AID, ALineNumber);
341 | end;
342 | end;
343 |
344 | function TCoveredMethods.Find(const AFileName: string; const AID: TMethodID; out Value: TCoveredMethod): Boolean;
345 | var
346 | list: TCoveredMethodList;
347 | begin
348 | Result := False;
349 | list := Find(AFileName);
350 | if list <> nil then begin
351 | Result := list.Find(AID, Value);
352 | end;
353 | end;
354 |
355 | function TCoveredMethods.Find(const AFileName: string): TCoveredMethodList;
356 | begin
357 | if not FData.TryGetValue(AFileName, Result) then begin
358 | Result := nil;
359 | end;
360 | end;
361 |
362 | function TCoveredMethods.FindOrAddList(const AFileName: string): TCoveredMethodList;
363 | begin
364 | if not FData.TryGetValue(AFileName, Result) then begin
365 | Result := TCoveredMethodList.Create;
366 | FData.Add(AFileName, Result);
367 | end;
368 | end;
369 |
370 | function TCoveredMethods.IsEmpty: Boolean;
371 | begin
372 | Result := (FData.Count = 0);
373 | end;
374 |
375 | procedure TCoveredMethods.Iterate(Callback: TCoveredMethodHandler);
376 | var
377 | pair: TPair;
378 | begin
379 | for pair in FData do begin
380 | pair.Value.Iterate(pair.Key, Callback);
381 | end;
382 | end;
383 |
384 | procedure TCoveredMethods.Remove(const AFileName: string; const AID: TMethodID);
385 | var
386 | list: TCoveredMethodList;
387 | begin
388 | list := Find(AFileName);
389 | if list <> nil then begin
390 | list.Remove(AID);
391 | if list.IsEmpty then begin
392 | FData.Remove(AFileName);
393 | end;
394 | end;
395 | end;
396 |
397 | procedure TCoveredMethods.Remove(const AFileName: string);
398 | begin
399 | FData.Remove(AFileName);
400 | end;
401 |
402 | procedure TCoveredMethods.RenameFile(const OldName, NewName: string);
403 | begin
404 | FData.RenameFile(OldName, NewName);
405 | end;
406 |
407 | procedure TCoveredMethods.Update(const AFileName: string; const AID: TMethodID; ALineMin, ALineMax: Integer);
408 | var
409 | list: TCoveredMethodList;
410 | begin
411 | list := Find(AFileName);
412 | if list <> nil then begin
413 | list.Update(AID, ALineMin, ALineMax);
414 | end;
415 | end;
416 |
417 | procedure TCoveredMethods.UpdatePercent(const AFileName: string; const AID: TMethodID; APercent: Integer);
418 | var
419 | list: TCoveredMethodList;
420 | begin
421 | list := Find(AFileName);
422 | if list <> nil then begin
423 | list.UpdatePercent(AID, APercent);
424 | end;
425 | end;
426 |
427 | constructor TFileNameDict.Create;
428 | begin
429 | inherited Create([doOwnsValues]);
430 | end;
431 |
432 | procedure TFileNameDict.RenameFile(const OldName, NewName: string);
433 | var
434 | pair: TPair;
435 | begin
436 | if ContainsKey(OldName) then begin
437 | if ContainsKey(NewName) then begin
438 | Remove(OldName);
439 | end
440 | else begin
441 | pair := ExtractPair(OldName);
442 | Add(NewName, pair.Value);
443 | end;
444 | end;
445 | end;
446 |
447 | end.
448 |
--------------------------------------------------------------------------------
/Source/DelphiAST/CodeCoverage.DelphiAST.Classes.pas:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UweRaabe/DelphiCodeCoveragePlugin/8f1246759e6ef6bb22c443c4e3b495d137319f98/Source/DelphiAST/CodeCoverage.DelphiAST.Classes.pas
--------------------------------------------------------------------------------
/Source/DelphiAST/CodeCoverage.DelphiAST.Consts.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.DelphiAST.Consts;
2 |
3 | interface
4 |
5 | type
6 | TSyntaxNodeType = (
7 | ntUnknown,
8 | ntAbsolute,
9 | ntAdd,
10 | ntAddr,
11 | ntAlignmentParam,
12 | ntAnd,
13 | ntAnonymousMethod,
14 | ntArguments,
15 | ntAs,
16 | ntAssign,
17 | ntAt,
18 | ntAttribute,
19 | ntAttributes,
20 | ntBounds,
21 | ntCall,
22 | ntCase,
23 | ntCaseElse,
24 | ntCaseLabel,
25 | ntCaseLabels,
26 | ntCaseSelector,
27 | ntClassConstraint,
28 | ntConstant,
29 | ntConstants,
30 | ntConstraints,
31 | ntConstructorConstraint,
32 | ntContains,
33 | ntDefault,
34 | ntDeref,
35 | ntDimension,
36 | ntDiv,
37 | ntDot,
38 | ntDownTo,
39 | ntElement,
40 | ntElse,
41 | ntEmptyStatement,
42 | ntEnum,
43 | ntEqual,
44 | ntExcept,
45 | ntExceptionHandler,
46 | ntExports,
47 | ntExpression,
48 | ntExpressions,
49 | ntExternal,
50 | ntFDiv,
51 | ntField,
52 | ntFields,
53 | ntFinalization,
54 | ntFinally,
55 | ntFor,
56 | ntFrom,
57 | ntGeneric,
58 | ntGoto,
59 | ntGreater,
60 | ntGreaterEqual,
61 | ntGuid,
62 | ntHelper,
63 | ntIdentifier,
64 | ntIf,
65 | ntImplementation,
66 | ntImplements,
67 | ntIn,
68 | ntIndex,
69 | ntIndexed,
70 | ntInherited,
71 | ntInitialization,
72 | ntInterface,
73 | ntIs,
74 | ntLabel,
75 | ntLHS,
76 | ntLiteral,
77 | ntLower,
78 | ntLowerEqual,
79 | ntMessage,
80 | ntMethod,
81 | ntMod,
82 | ntMul,
83 | ntName,
84 | ntNamedArgument,
85 | ntNotEqual,
86 | ntNot,
87 | ntOr,
88 | ntPackage,
89 | ntParameter,
90 | ntParameters,
91 | ntPath,
92 | ntPositionalArgument,
93 | ntProtected,
94 | ntPrivate,
95 | ntProperty,
96 | ntPublic,
97 | ntPublished,
98 | ntRaise,
99 | ntRead,
100 | ntRecordConstraint,
101 | ntRepeat,
102 | ntRequires,
103 | ntResolutionClause,
104 | ntResourceString,
105 | ntReturnType,
106 | ntRHS,
107 | ntRoundClose,
108 | ntRoundOpen,
109 | ntSet,
110 | ntShl,
111 | ntShr,
112 | ntStatement,
113 | ntStatements,
114 | ntStrictPrivate,
115 | ntStrictProtected,
116 | ntSub,
117 | ntSubrange,
118 | ntThen,
119 | ntTo,
120 | ntTry,
121 | ntType,
122 | ntTypeArgs,
123 | ntTypeDecl,
124 | ntTypeParam,
125 | ntTypeParams,
126 | ntTypeSection,
127 | ntValue,
128 | ntVariable,
129 | ntVariables,
130 | ntXor,
131 | ntUnaryMinus,
132 | ntUnit,
133 | ntUses,
134 | ntWhile,
135 | ntWith,
136 | ntWrite,
137 |
138 | ntAnsiComment,
139 | ntBorComment,
140 | ntSlashesComment
141 | );
142 |
143 | TAttributeName = (
144 | anType,
145 | anClass,
146 | anForwarded,
147 | anKind,
148 | anName,
149 | anVisibility,
150 | anCallingConvention,
151 | anPath,
152 | anMethodBinding,
153 | anReintroduce,
154 | anOverload,
155 | anAbstract,
156 | anInline,
157 | anAlign
158 | );
159 |
160 | const
161 | SyntaxNodeNames: array [TSyntaxNodeType] of string = (
162 | 'unknown',
163 | 'absolute',
164 | 'add',
165 | 'addr',
166 | 'alignmentparam',
167 | 'and',
168 | 'anonymousmethod',
169 | 'arguments',
170 | 'as',
171 | 'assign',
172 | 'at',
173 | 'attribute',
174 | 'attributes',
175 | 'bounds',
176 | 'call',
177 | 'case',
178 | 'caseelse',
179 | 'caselabel',
180 | 'caselabels',
181 | 'caseselector',
182 | 'classconstraint',
183 | 'constant',
184 | 'constants',
185 | 'constraints',
186 | 'constructorconstraint',
187 | 'contains',
188 | 'default',
189 | 'deref',
190 | 'dimension',
191 | 'div',
192 | 'dot',
193 | 'downto',
194 | 'element',
195 | 'else',
196 | 'emptystatement',
197 | 'enum',
198 | 'equal',
199 | 'except',
200 | 'exceptionhandler',
201 | 'exports',
202 | 'expression',
203 | 'expressions',
204 | 'external',
205 | 'fdiv',
206 | 'field',
207 | 'fields',
208 | 'finalization',
209 | 'finally',
210 | 'for',
211 | 'from',
212 | 'generic',
213 | 'goto',
214 | 'greater',
215 | 'greaterequal',
216 | 'guid',
217 | 'helper',
218 | 'identifier',
219 | 'if',
220 | 'implementation',
221 | 'implements',
222 | 'in',
223 | 'index',
224 | 'indexed',
225 | 'inherited',
226 | 'initialization',
227 | 'interface',
228 | 'is',
229 | 'label',
230 | 'lhs',
231 | 'literal',
232 | 'lower',
233 | 'lowerequal',
234 | 'message',
235 | 'method',
236 | 'mod',
237 | 'mul',
238 | 'name',
239 | 'namedargument',
240 | 'notequal',
241 | 'not',
242 | 'or',
243 | 'package',
244 | 'parameter',
245 | 'parameters',
246 | 'path',
247 | 'positionalargument',
248 | 'protected',
249 | 'private',
250 | 'property',
251 | 'public',
252 | 'published',
253 | 'raise',
254 | 'read',
255 | 'recordconstraint',
256 | 'repeat',
257 | 'requires',
258 | 'resolutionclause',
259 | 'resourcestring',
260 | 'returntype',
261 | 'rhs',
262 | 'roundclose',
263 | 'roundopen',
264 | 'set',
265 | 'shl',
266 | 'shr',
267 | 'statement',
268 | 'statements',
269 | 'strictprivate',
270 | 'strictprotected',
271 | 'sub',
272 | 'subrange',
273 | 'then',
274 | 'to',
275 | 'try',
276 | 'type',
277 | 'typeargs',
278 | 'typedecl',
279 | 'typeparam',
280 | 'typeparams',
281 | 'typesection',
282 | 'value',
283 | 'variable',
284 | 'variables',
285 | 'xor',
286 | 'unaryminus',
287 | 'unit',
288 | 'uses',
289 | 'while',
290 | 'with',
291 | 'write',
292 |
293 | 'ansicomment',
294 | 'borlandcomment',
295 | 'slashescomment'
296 | );
297 |
298 | AttributeNameStrings: array[TAttributeName] of string = (
299 | 'type',
300 | 'class',
301 | 'forwarded',
302 | 'kind',
303 | 'name',
304 | 'visibility',
305 | 'callingconvention',
306 | 'path',
307 | 'methodbinding',
308 | 'reintroduce',
309 | 'overload',
310 | 'abstract',
311 | 'inline',
312 | 'align'
313 | );
314 |
315 | implementation
316 |
317 | end.
318 |
--------------------------------------------------------------------------------
/Source/DelphiAST/CodeCoverage.DelphiAST.SimpleParserEx.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.DelphiAST.SimpleParserEx;
2 |
3 | interface
4 |
5 | uses
6 | SysUtils, Generics.Collections, SimpleParser, SimpleParser.Lexer.Types,
7 | SimpleParser.Lexer, Classes;
8 |
9 | type
10 | TStringEvent = procedure(var s: string) of object;
11 |
12 | TPasLexer = class
13 | private
14 | FLexer: TmwPasLex;
15 | FOnHandleString: TStringEvent;
16 | function GetToken: string; inline;
17 | function GetPosXY: TTokenPoint; inline;
18 | function GetFileName: string;
19 | public
20 | constructor Create(const ALexer: TmwPasLex; AOnHandleString: TStringEvent);
21 | property FileName: string read GetFileName;
22 | property PosXY: TTokenPoint read GetPosXY;
23 | property Token: string read GetToken;
24 | property Lexer: TmwPasLex read FLexer;
25 | end;
26 |
27 | TmwSimplePasParEx = class(TmwSimplePasPar)
28 | public type
29 | TNameListStack = class;
30 | TNameList = class
31 | public type
32 | TNameItem = class
33 | public type
34 | TNameItemToken = class
35 | strict private
36 | FTokenFileName: string;
37 | FTokenPoint: TTokenPoint;
38 | FTokenPos: Integer;
39 | FTokenLen: Integer;
40 | public
41 | constructor Create(const ATokenFileName: string;
42 | const ATokenPoint: TTokenPoint; const ATokenPos, ATokenLen: Integer);
43 | property TokenFileName: string read FTokenFileName;
44 | property TokenPoint: TTokenPoint read FTokenPoint;
45 | property TokenPos: Integer read FTokenPos;
46 | property TokenLen: Integer read FTokenLen;
47 | end;
48 | strict private
49 | FTokenList: TObjectList;
50 | FEndNameCalled: Boolean;
51 | function GetLastNameItemToken: TNameItemToken;
52 | public
53 | constructor Create(const ATokenFileName: string;
54 | const ATokenPoint: TTokenPoint; const ATokenID: TptTokenKind;
55 | const ATokenPos, ATokenLen: Integer);
56 | destructor Destroy; override;
57 | procedure AddToken(const ATokenFileName: string;
58 | const ATokenPoint: TTokenPoint; const ATokenID: TptTokenKind;
59 | const ATokenPos, ATokenLen: Integer);
60 | property TokenList: TObjectList read FTokenList;
61 | property LastNameItemToken: TNameItemToken read GetLastNameItemToken;
62 | property EndNameCalled: Boolean read FEndNameCalled write FEndNameCalled;
63 | end;
64 | strict private
65 | FParser: TmwSimplePasParEx;
66 | FNameItems: TObjectList;
67 | FAutoCreated: Boolean;
68 | function GetItems(const Index: Integer): TNameItem; inline;
69 | function GetLastItem: TNameItem; inline;
70 | function GetOriginalNames(const Index: Integer): string;
71 | function GetNames(const Index: Integer): string;
72 | function GetLastOriginalName: string;
73 | function GetLastName: string;
74 | function GetCount: Integer; inline;
75 | function GetLexer: TmwPasLex; inline;
76 | property Lexer: TmwPasLex read GetLexer;
77 | public
78 | constructor Create(const AParser: TmwSimplePasParEx;
79 | const AAutoCreated: Boolean);
80 | destructor Destroy; override;
81 | procedure BeginName;
82 | procedure EndName;
83 | procedure AddToken;
84 | property AutoCreated: Boolean read FAutoCreated;
85 | property Items[const Index: Integer]: TNameItem read GetItems;
86 | property LastItem: TNameItem read GetLastItem;
87 | property OriginalNames[const Index: Integer]: string read GetOriginalNames;
88 | property Names[const Index: Integer]: string read GetNames; default;
89 | property LastOriginalName: string read GetLastOriginalName;
90 | property LastName: string read GetLastName;
91 | property Count: Integer read GetCount;
92 | end;
93 | TNameListStack = class
94 | strict private
95 | FParser: TmwSimplePasParEx;
96 | FNameListStack: TObjectStack;
97 | public
98 | constructor Create(const AParser: TmwSimplePasParEx);
99 | destructor Destroy; override;
100 | procedure PushNames(const AAutoCreated: Boolean); inline;
101 | procedure PopNames; inline;
102 | function ExtractNames: TNameList; inline;
103 | function PeekNames: TNameList; inline;
104 | function ToArray: TArray; inline;
105 | function Count: Integer; inline;
106 | end;
107 | strict private
108 | FNameListStack: TNameListStack;
109 | FPreviousNames: TNameList;
110 | FLexer: TPasLexer;
111 | FLowerCaseNames: Boolean;
112 | FOnHandleString: TStringEvent;
113 | function GetCurrentNames: TNameList; inline;
114 | strict protected
115 | procedure DoHandleString(var AString: string); inline;
116 | procedure PushNames; inline;
117 | procedure PopNames; inline;
118 | function PeekNames: TNameList; inline;
119 | procedure BeginName;
120 | procedure EndName;
121 | property CurrentNames: TNameList read GetCurrentNames;
122 | property PreviousNames: TNameList read FPreviousNames;
123 | protected
124 | procedure NextToken; override;
125 | public
126 | constructor Create; override;
127 | destructor Destroy; override;
128 | property Lexer: TPasLexer read FLexer;
129 | property LowerCaseNames: Boolean read FLowerCaseNames write FLowerCaseNames;
130 | property OnHandleString: TStringEvent read FOnHandleString write FOnHandleString;
131 | end;
132 |
133 | implementation
134 |
135 | { TPasLexer }
136 |
137 | constructor TPasLexer.Create(const ALexer: TmwPasLex; AOnHandleString: TStringEvent);
138 | begin
139 | inherited Create;
140 | FLexer := ALexer;
141 | FOnHandleString := AOnHandleString;
142 | end;
143 |
144 | function TPasLexer.GetFileName: string;
145 | begin
146 | Result := FLexer.Buffer.FileName;
147 | end;
148 |
149 | function TPasLexer.GetPosXY: TTokenPoint;
150 | begin
151 | Result := FLexer.PosXY;
152 | end;
153 |
154 | function TPasLexer.GetToken: string;
155 | begin
156 | Result := FLexer.Token;
157 | FOnHandleString(Result);
158 | end;
159 |
160 | { TmwSimplePasParEx.TNameList.TNameItem.TNameItemToken }
161 |
162 | constructor TmwSimplePasParEx.TNameList.TNameItem.TNameItemToken.Create(
163 | const ATokenFileName: string; const ATokenPoint: TTokenPoint;
164 | const ATokenPos, ATokenLen: Integer);
165 | begin
166 | FTokenFileName := ATokenFileName;
167 | FTokenPoint := ATokenPoint;
168 | FTokenPos := ATokenPos;
169 | FTokenLen := ATokenLen;
170 | end;
171 |
172 | { TPasNamesBuilder.TNamesList.TNameItem }
173 |
174 | constructor TmwSimplePasParEx.TNameList.TNameItem.Create(
175 | const ATokenFileName: string; const ATokenPoint: TTokenPoint;
176 | const ATokenID: TptTokenKind; const ATokenPos, ATokenLen: Integer);
177 | begin
178 | FTokenList := TObjectList.Create(True);
179 | AddToken(ATokenFileName, ATokenPoint, ATokenID, ATokenPos, ATokenLen);
180 | end;
181 |
182 | destructor TmwSimplePasParEx.TNameList.TNameItem.Destroy;
183 | begin
184 | FTokenList.Free;
185 | inherited;
186 | end;
187 |
188 | procedure TmwSimplePasParEx.TNameList.TNameItem.AddToken(
189 | const ATokenFileName: string; const ATokenPoint: TTokenPoint;
190 | const ATokenID: TptTokenKind; const ATokenPos, ATokenLen: Integer);
191 | begin
192 | if not IsTokenIDJunk(ATokenID) and
193 | ((FTokenList.Count = 0) or (FTokenList.Last.TokenPos < ATokenPos)) then
194 | FTokenList.Add(TNameItemToken.Create(
195 | ATokenFileName, ATokenPoint, ATokenPos, ATokenLen));
196 | end;
197 |
198 | function TmwSimplePasParEx.TNameList.TNameItem.GetLastNameItemToken: TNameItemToken;
199 | begin
200 | Result := TokenList.Last;
201 | end;
202 |
203 | { TPasNamesBuilder.TNamesList }
204 |
205 | constructor TmwSimplePasParEx.TNameList.Create(const AParser: TmwSimplePasParEx;
206 | const AAutoCreated: Boolean);
207 | begin
208 | FParser := AParser;
209 | FNameItems := TObjectList.Create(True);
210 | end;
211 |
212 | destructor TmwSimplePasParEx.TNameList.Destroy;
213 | begin
214 | FNameItems.Free;
215 | inherited;
216 | end;
217 |
218 | function TmwSimplePasParEx.TNameList.GetLexer: TmwPasLex;
219 | begin
220 | Result := FParser.Lexer.Lexer;
221 | end;
222 |
223 | procedure TmwSimplePasParEx.TNameList.BeginName;
224 | begin
225 | FNameItems.Add(TNameItem.Create(Lexer.FileName, Lexer.PosXY, Lexer.TokenID,
226 | Lexer.TokenPos, Lexer.TokenLen));
227 | end;
228 |
229 | procedure TmwSimplePasParEx.TNameList.EndName;
230 | begin
231 | FNameItems.Last.EndNameCalled := True;
232 | end;
233 |
234 | procedure TmwSimplePasParEx.TNameList.AddToken;
235 | begin
236 | FNameItems.Last.AddToken(Lexer.FileName, Lexer.PosXY, Lexer.TokenID,
237 | Lexer.TokenPos, Lexer.TokenLen);
238 | end;
239 |
240 | function TmwSimplePasParEx.TNameList.GetItems(const Index: Integer): TNameItem;
241 | begin
242 | Result := FNameItems[Index];
243 | end;
244 |
245 | function TmwSimplePasParEx.TNameList.GetLastItem: TNameItem;
246 | begin
247 | Result := FNameItems.Last;
248 | end;
249 |
250 | function TmwSimplePasParEx.TNameList.GetOriginalNames(const Index: Integer): string;
251 | var
252 | I: Integer;
253 | NameItem: TNameItem;
254 | Token: string;
255 | begin
256 | Result := '';
257 | NameItem := Items[Index];
258 | for I := 0 to NameItem.TokenList.Count - 1 do
259 | begin
260 | SetString(Token, Lexer.Buffer.Buf + NameItem.TokenList[I].TokenPos,
261 | NameItem.TokenList[I].TokenLen);
262 | Result := Result + Token;
263 | end;
264 | FParser.DoHandleString(Result);
265 | end;
266 |
267 | function TmwSimplePasParEx.TNameList.GetNames(const Index: Integer): string;
268 | begin
269 | Result := OriginalNames[Index];
270 | if FParser.LowerCaseNames then
271 | begin
272 | Result := AnsiLowerCase(Result);
273 | FParser.DoHandleString(Result);
274 | end;
275 | end;
276 |
277 | function TmwSimplePasParEx.TNameList.GetLastOriginalName: string;
278 | begin
279 | Result := OriginalNames[Count - 1];
280 | end;
281 |
282 | function TmwSimplePasParEx.TNameList.GetLastName: string;
283 | begin
284 | Result := Names[Count - 1];
285 | end;
286 |
287 | function TmwSimplePasParEx.TNameList.GetCount: Integer;
288 | begin
289 | Result := FNameItems.Count;
290 | end;
291 |
292 | { TPasNamesBuilder.TNameListStack }
293 |
294 | function TmwSimplePasParEx.TNameListStack.Count: Integer;
295 | begin
296 | Result := FNameListStack.Count;
297 | end;
298 |
299 | constructor TmwSimplePasParEx.TNameListStack.Create(
300 | const AParser: TmwSimplePasParEx);
301 | begin
302 | FParser := AParser;
303 | FNameListStack := TObjectStack.Create(True);
304 | end;
305 |
306 | destructor TmwSimplePasParEx.TNameListStack.Destroy;
307 | begin
308 | FNameListStack.Free;
309 | inherited;
310 | end;
311 |
312 | procedure TmwSimplePasParEx.TNameListStack.PushNames(const AAutoCreated: Boolean);
313 | begin
314 | FNameListStack.Push(TNameList.Create(FParser, AAutoCreated));
315 | end;
316 |
317 | procedure TmwSimplePasParEx.TNameListStack.PopNames;
318 | begin
319 | FNameListStack.Pop;
320 | end;
321 |
322 | function TmwSimplePasParEx.TNameListStack.ExtractNames: TNameList;
323 | begin
324 | Result := FNameListStack.Extract;
325 | end;
326 |
327 | function TmwSimplePasParEx.TNameListStack.PeekNames: TNameList;
328 | begin
329 | Result := FNameListStack.Peek;
330 | end;
331 |
332 | function TmwSimplePasParEx.TNameListStack.ToArray: TArray;
333 | begin
334 | Result := FNameListStack.ToArray;
335 | end;
336 |
337 | { TmwSimplePasParEx }
338 |
339 | constructor TmwSimplePasParEx.Create;
340 | begin
341 | inherited;
342 | FNameListStack := TNameListStack.Create(Self);
343 | FNameListStack.PushNames(True);
344 | FPreviousNames := TNameList.Create(Self, True);
345 | FLexer := TPasLexer.Create(inherited Lexer, DoHandleString);
346 | FLowerCaseNames := True;
347 | end;
348 |
349 | destructor TmwSimplePasParEx.Destroy;
350 | begin
351 | FLexer.Free;
352 | FPreviousNames.Free;
353 | FNameListStack.PopNames;
354 | FNameListStack.Free;
355 | inherited;
356 | end;
357 |
358 | procedure TmwSimplePasParEx.NextToken;
359 | var
360 | NameList: TNameList;
361 | begin
362 | if FNameListStack.Count > 0 then
363 | for NameList in FNameListStack.ToArray do
364 | if (NameList.Count > 0) and not NameList.LastItem.EndNameCalled then
365 | NameList.AddToken;
366 | inherited;
367 | end;
368 |
369 | procedure TmwSimplePasParEx.PushNames;
370 | begin
371 | FNameListStack.PushNames(False);
372 | end;
373 |
374 | procedure TmwSimplePasParEx.PopNames;
375 | begin
376 | FPreviousNames.Free;
377 | FPreviousNames := FNameListStack.ExtractNames;
378 | end;
379 |
380 | function TmwSimplePasParEx.PeekNames: TNameList;
381 | begin
382 | Result := FNameListStack.PeekNames;
383 | end;
384 |
385 | procedure TmwSimplePasParEx.BeginName;
386 | var
387 | NameList: TNameList;
388 | begin
389 | NameList := FNameListStack.PeekNames;
390 | if (NameList.Count > 0) and not NameList.LastItem.EndNameCalled then
391 | begin
392 | FNameListStack.PushNames(True);
393 | NameList := FNameListStack.PeekNames;
394 | end;
395 | NameList.BeginName;
396 | end;
397 |
398 | procedure TmwSimplePasParEx.EndName;
399 | var
400 | NameList: TNameList;
401 | begin
402 | NameList := FNameListStack.PeekNames;
403 | if NameList.LastItem.EndNameCalled then
404 | begin
405 | FNameListStack.PopNames;
406 | NameList := FNameListStack.PeekNames;
407 | end;
408 | NameList.EndName;
409 | end;
410 |
411 | procedure TmwSimplePasParEx.DoHandleString(var AString: string);
412 | begin
413 | if Assigned(FOnHandleString) then
414 | FOnHandleString(AString);
415 | end;
416 |
417 | function TmwSimplePasParEx.GetCurrentNames: TNameList;
418 | begin
419 | Result := PeekNames;
420 | end;
421 |
422 | end.
423 |
--------------------------------------------------------------------------------
/Source/DelphiAST/CodeCoverage.SimpleParser.Lexer.Types.pas:
--------------------------------------------------------------------------------
1 | {---------------------------------------------------------------------------
2 | The contents of this file are subject to the Mozilla Public License Version
3 | 1.1 (the "License"); you may not use this file except in compliance with the
4 | License. You may obtain a copy of the License at
5 | http://www.mozilla.org/NPL/NPL-1_1Final.html
6 |
7 | Software distributed under the License is distributed on an "AS IS" basis,
8 | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9 | the specific language governing rights and limitations under the License.
10 |
11 | The Original Code is: mwPasLexTypes, released November 14, 1999.
12 |
13 | The Initial Developer of the Original Code is Martin Waldenburg
14 | unit CastaliaPasLexTypes;
15 |
16 | ----------------------------------------------------------------------------}
17 |
18 | unit CodeCoverage.SimpleParser.Lexer.Types;
19 |
20 | {$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
21 |
22 | interface
23 |
24 | uses
25 | SysUtils,
26 | TypInfo;
27 |
28 | {$INCLUDE SimpleParser.inc}
29 |
30 | {$IFNDEF D14_NEWER}
31 | type
32 | TArray = array of T;
33 | {$ENDIF}
34 |
35 | var
36 | CompTable: array[#0..#255] of byte;
37 |
38 | type
39 | TMessageEventType = (meError, meNotSupported);
40 |
41 | TMessageEvent = procedure(Sender: TObject; const Typ: TMessageEventType;
42 | const Msg: string; X, Y: Integer) of object;
43 |
44 | TCommentState = (csAnsi, csBor, csNo);
45 |
46 | TTokenPoint = packed record
47 | X: Integer;
48 | Y: Integer;
49 | end;
50 |
51 | TptTokenKind = (
52 | ptAbort,
53 | ptAbsolute,
54 | ptAbstract,
55 | ptAdd,
56 | ptAddressOp,
57 | ptAlign,
58 | ptAmpersand,
59 | ptAnd,
60 | ptAnsiComment,
61 | ptAnsiString,
62 | ptArray,
63 | ptAs,
64 | ptAsciiChar,
65 | ptAsm,
66 | ptAssembler,
67 | ptAssign,
68 | ptAt,
69 | ptAutomated,
70 | ptBegin,
71 | ptBoolean,
72 | ptBorComment,
73 | ptBraceClose,
74 | ptBraceOpen,
75 | ptBreak,
76 | ptByte,
77 | ptByteBool,
78 | ptCardinal,
79 | ptCase,
80 | ptCdecl,
81 | ptChar,
82 | ptClass,
83 | ptClassForward,
84 | ptClassFunction,
85 | ptClassProcedure,
86 | ptColon,
87 | ptComma,
88 | ptComp,
89 | ptCompDirect,
90 | ptConst,
91 | ptConstructor,
92 | ptContains,
93 | ptContinue,
94 | ptCRLF,
95 | ptCRLFCo,
96 | ptCurrency,
97 | ptDefault,
98 | ptDefineDirect,
99 | ptDeprecated,
100 | ptDestructor,
101 | ptDispid,
102 | ptDispinterface,
103 | ptDiv,
104 | ptDo,
105 | ptDotDot,
106 | ptDouble,
107 | ptDoubleAddressOp,
108 | ptDownto,
109 | ptDWORD,
110 | ptDynamic,
111 | ptElse,
112 | ptElseDirect,
113 | ptEnd,
114 | ptEndIfDirect,
115 | ptEqual,
116 | ptError,
117 | ptExcept,
118 | ptExit,
119 | ptExport,
120 | ptExports,
121 | ptExtended,
122 | ptExternal,
123 | ptFar,
124 | ptFile,
125 | ptFinal,
126 | ptExperimental,
127 | ptDelayed,
128 | ptFinalization,
129 | ptFinally,
130 | ptFloat,
131 | ptFor,
132 | ptForward,
133 | ptFunction,
134 | ptGoto,
135 | ptGreater,
136 | ptGreaterEqual,
137 | ptHalt,
138 | ptHelper,
139 | ptIdentifier,
140 | ptIf,
141 | ptIfDirect,
142 | ptIfEndDirect,
143 | ptElseIfDirect,
144 | ptIfDefDirect,
145 | ptIfNDefDirect,
146 | ptIfOptDirect,
147 | ptImplementation,
148 | ptImplements,
149 | ptIn,
150 | ptIncludeDirect,
151 | ptIndex,
152 | ptInherited,
153 | ptInitialization,
154 | ptInline,
155 | ptInt64,
156 | ptInteger,
157 | ptIntegerConst,
158 | ptInterface,
159 | ptIs,
160 | ptLabel,
161 | ptLibrary,
162 | ptLocal,
163 | ptLongBool,
164 | ptLongint,
165 | ptLongword,
166 | ptLower,
167 | ptLowerEqual,
168 | ptMessage,
169 | ptMinus,
170 | ptMod,
171 | ptName,
172 | ptNear,
173 | ptNil,
174 | ptNodefault,
175 | ptNone,
176 | ptNot,
177 | ptNotEqual,
178 | ptNull,
179 | ptObject,
180 | ptOf,
181 | ptOleVariant,
182 | ptOn,
183 | ptOperator,
184 | ptOr,
185 | ptOut,
186 | ptOverload,
187 | ptOverride,
188 | ptPackage,
189 | ptPacked,
190 | ptPascal,
191 | ptPChar,
192 | ptPlatform,
193 | ptPlus,
194 | ptPoint,
195 | ptPointerSymbol,
196 | ptPrivate,
197 | ptProcedure,
198 | ptProgram,
199 | ptProperty,
200 | ptProtected,
201 | ptPublic,
202 | ptPublished,
203 | ptRaise,
204 | ptRead,
205 | ptReadonly,
206 | ptReal,
207 | ptReal48,
208 | ptRecord,
209 | ptReference,
210 | ptRegister,
211 | ptReintroduce,
212 | ptRemove,
213 | ptRepeat,
214 | ptRequires,
215 | ptResident,
216 | ptResourceDirect,
217 | ptResourcestring,
218 | ptRoundClose,
219 | ptRoundOpen,
220 | ptRunError,
221 | ptSafeCall,
222 | ptScopedEnumsDirect,
223 | ptSealed,
224 | ptSemiColon,
225 | ptSet,
226 | ptShl,
227 | ptShortint,
228 | ptShortString,
229 | ptShr,
230 | ptSingle,
231 | ptSlash,
232 | ptSlashesComment,
233 | ptSmallint,
234 | ptSpace,
235 | ptSquareClose,
236 | ptSquareOpen,
237 | ptStar,
238 | ptStatic,
239 | ptStdcall,
240 | ptStored,
241 | ptStrict,
242 | ptString,
243 | ptStringConst,
244 | ptStringDQConst,
245 | ptStringresource,
246 | ptSymbol,
247 | ptThen,
248 | ptThreadvar,
249 | ptTo,
250 | ptTry,
251 | ptType,
252 | ptUndefDirect,
253 | ptUnit,
254 | ptUnknown,
255 | ptUnsafe,
256 | ptUntil,
257 | ptUses,
258 | ptVar,
259 | ptVarargs,
260 | ptVariant,
261 | ptVirtual,
262 | ptWhile,
263 | ptWideChar,
264 | ptWideString,
265 | ptWith,
266 | ptWord,
267 | ptWordBool,
268 | ptWrite,
269 | ptWriteonly,
270 | ptXor);
271 |
272 | TmwPasLexStatus = record
273 | CommentState: TCommentState;
274 | ExID: TptTokenKind;
275 | LineNumber: Integer;
276 | LinePos: Integer;
277 | Origin: PChar;
278 | RunPos: Integer;
279 | TokenPos: Integer;
280 | TokenID: TptTokenKind;
281 | end;
282 |
283 | EIncludeError = class(Exception);
284 | IIncludeHandler = interface
285 | ['{C5F20740-41D2-43E9-8321-7FE5E3AA83B6}']
286 | function GetIncludeFileContent(const ParentFileName, IncludeName: string;
287 | out Content: string; out FileName: string): Boolean;
288 | end;
289 |
290 | function TokenName(Value: TptTokenKind): string;
291 | function ptTokenName(Value: TptTokenKind): string;
292 | function IsTokenIDJunk(const aTokenID: TptTokenKind): Boolean;
293 |
294 | implementation
295 |
296 | function TokenName(Value: TptTokenKind): string;
297 | begin
298 | Result := Copy(ptTokenName(Value), 3, MaxInt);
299 | end;
300 |
301 | function ptTokenName(Value: TptTokenKind): string;
302 | begin
303 | result := GetEnumName(TypeInfo(TptTokenKind), Integer(Value));
304 | end;
305 |
306 | function IsTokenIDJunk(const aTokenID: TptTokenKind): Boolean;
307 | begin
308 | Result := aTokenID in [
309 | ptAnsiComment,
310 | ptBorComment,
311 | ptCRLF,
312 | ptCRLFCo,
313 | ptSlashesComment,
314 | ptSpace,
315 | ptIfDirect,
316 | ptElseDirect,
317 | ptIfEndDirect,
318 | ptElseIfDirect,
319 | ptIfDefDirect,
320 | ptIfNDefDirect,
321 | ptEndIfDirect,
322 | ptIfOptDirect,
323 | ptDefineDirect,
324 | ptScopedEnumsDirect,
325 | ptUndefDirect];
326 | end;
327 |
328 | end.
329 |
--------------------------------------------------------------------------------
/Source/DelphiAST/CodeCoverage.SimpleParser.Types.pas:
--------------------------------------------------------------------------------
1 | {---------------------------------------------------------------------------
2 | The contents of this file are subject to the Mozilla Public License Version
3 | 1.1 (the "License"); you may not use this file except in compliance with the
4 | License. You may obtain a copy of the License at
5 | http://www.mozilla.org/NPL/NPL-1_1Final.html
6 |
7 | Software distributed under the License is distributed on an "AS IS" basis,
8 | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9 | the specific language governing rights and limitations under the License.
10 |
11 | The Original Code is: mwSimplePasParTypes, released November 14, 1999.
12 |
13 | The Initial Developer of the Original Code is Martin Waldenburg
14 | unit CastaliaPasLexTypes;
15 |
16 | ----------------------------------------------------------------------------}
17 |
18 | unit CodeCoverage.SimpleParser.Types;
19 |
20 | interface
21 |
22 | uses
23 | SysUtils,
24 | TypInfo;
25 |
26 | type
27 | TmwParseError = (
28 | InvalidAdditiveOperator,
29 | InvalidAccessSpecifier,
30 | InvalidCharString,
31 | InvalidClassMethodHeading,
32 | InvalidConstantDeclaration,
33 | InvalidConstSection,
34 | InvalidDeclarationSection,
35 | InvalidDirective16Bit,
36 | InvalidDirectiveBinding,
37 | InvalidDirectiveCalling,
38 | InvalidExportedHeading,
39 | InvalidForStatement,
40 | InvalidInitializationSection,
41 | InvalidInterfaceDeclaration,
42 | InvalidInterfaceType,
43 | InvalidLabelId,
44 | InvalidLabeledStatement,
45 | InvalidMethodHeading,
46 | InvalidMultiplicativeOperator,
47 | InvalidNumber,
48 | InvalidOrdinalIdentifier,
49 | InvalidParameter,
50 | InvalidParseFile,
51 | InvalidProceduralDirective,
52 | InvalidProceduralType,
53 | InvalidProcedureDeclarationSection,
54 | InvalidProcedureMethodDeclaration,
55 | InvalidRealIdentifier,
56 | InvalidRelativeOperator,
57 | InvalidStorageSpecifier,
58 | InvalidStringIdentifier,
59 | InvalidStructuredType,
60 | InvalidTryStatement,
61 | InvalidTypeKind,
62 | InvalidVariantIdentifier,
63 | InvalidVarSection,
64 | vchInvalidClass,
65 | vchInvalidMethod,
66 | vchInvalidProcedure,
67 | vchInvalidCircuit,
68 | vchInvalidIncludeFile
69 | );
70 |
71 | TmwPasCodeInfo = (
72 | ciNone,
73 | ciAccessSpecifier,
74 | ciAdditiveOperator,
75 | ciArrayConstant,
76 | ciArrayType,
77 | ciAsmStatement,
78 | ciBlock,
79 | ciCaseLabel,
80 | ciCaseSelector,
81 | ciCaseStatement,
82 | ciCharString,
83 | ciClassClass,
84 | ciClassField,
85 | ciClassForward,
86 | ciClassFunctionHeading,
87 | ciClassHeritage,
88 | ciClassMemberList,
89 | ciClassMethodDirective,
90 | ciClassMethodHeading,
91 | ciClassMethodOrProperty,
92 | ciClassMethodResolution,
93 | ciClassProcedureHeading,
94 | ciClassProperty,
95 | ciClassReferenceType,
96 | ciClassType,
97 | ciClassTypeEnd,
98 | ciClassVisibility,
99 | ciCompoundStatement,
100 | ciConstantColon,
101 | ciConstantDeclaration,
102 | ciConstantEqual,
103 | ciConstantExpression,
104 | ciConstantName,
105 | ciConstantValue,
106 | ciConstantValueTyped,
107 | ciConstParameter,
108 | ciConstructorHeading,
109 | ciConstructorName,
110 | ciConstSection,
111 | ciContainsClause,
112 | ciContainsExpression,
113 | ciContainsIdentifier,
114 | ciContainsStatement,
115 | ciDeclarationSection,
116 | ciDesignator,
117 | ciDestructorHeading,
118 | ciDestructorName,
119 | ciDirective16Bit,
120 | ciDirectiveBinding,
121 | ciDirectiveCalling,
122 | ciDirectiveDeprecated,
123 | ciDirectiveLibrary,
124 | ciDirectiveLocal,
125 | ciDirectivePlatform,
126 | ciDirectiveVarargs,
127 | ciDispIDSpecifier,
128 | ciDispInterfaceForward,
129 | ciEmptyStatement,
130 | ciEnumeratedType,
131 | ciEnumeratedTypeItem,
132 | ciExceptBlock,
133 | ciExceptionBlockElseBranch,
134 | ciExceptionClassTypeIdentifier,
135 | ciExceptionHandler,
136 | ciExceptionHandlerList,
137 | ciExceptionIdentifier,
138 | ciExceptionVariable,
139 | ciExpliciteType,
140 | ciExportedHeading,
141 | ciExportsClause,
142 | ciExportsElement,
143 | ciExpression,
144 | ciExpressionList,
145 | ciExternalDirective,
146 | ciExternalDirectiveThree,
147 | ciExternalDirectiveTwo,
148 | ciFactor,
149 | ciFieldDeclaration,
150 | ciFieldList,
151 | ciFileType,
152 | ciFormalParameterList,
153 | ciFormalParameterSection,
154 | ciForStatement,
155 | ciForwardDeclaration,
156 | ciFunctionHeading,
157 | ciFunctionMethodDeclaration,
158 | ciFunctionMethodName,
159 | ciFunctionProcedureBlock,
160 | ciFunctionProcedureName,
161 | ciHandlePtCompDirect,
162 | ciHandlePtDefineDirect,
163 | ciHandlePtElseDirect,
164 | ciHandlePtIfDefDirect,
165 | ciHandlePtEndIfDirect,
166 | ciHandlePtIfNDefDirect,
167 | ciHandlePtIfOptDirect,
168 | ciHandlePtIncludeDirect,
169 | ciHandlePtResourceDirect,
170 | ciHandlePtUndefDirect,
171 | ciIdentifier,
172 | ciIdentifierList,
173 | ciIfStatement,
174 | ciImplementationSection,
175 | ciIncludeFile,
176 | ciIndexSpecifier,
177 | ciInheritedStatement,
178 | ciInitializationSection,
179 | ciInlineStatement,
180 | ciInterfaceDeclaration,
181 | ciInterfaceForward,
182 | ciInterfaceGUID,
183 | ciInterfaceHeritage,
184 | ciInterfaceMemberList,
185 | ciInterfaceSection,
186 | ciInterfaceType,
187 | ciLabelDeclarationSection,
188 | ciLabeledStatement,
189 | ciLabelId,
190 | ciLibraryFile,
191 | ciMainUsedUnitExpression,
192 | ciMainUsedUnitName,
193 | ciMainUsedUnitStatement,
194 | ciMainUsesClause,
195 | ciMultiplicativeOperator,
196 | ciNewFormalParameterType,
197 | ciNumber,
198 | ciNextToken,
199 | ciObjectConstructorHeading,
200 | ciObjectDestructorHeading,
201 | ciObjectField,
202 | ciObjectForward,
203 | ciObjectFunctionHeading,
204 | ciObjectHeritage,
205 | ciObjectMemberList,
206 | ciObjectMethodDirective,
207 | ciObjectMethodHeading,
208 | ciObjectNameOfMethod,
209 | ciObjectProcedureHeading,
210 | ciObjectProperty,
211 | ciObjectPropertySpecifiers,
212 | ciObjectType,
213 | ciObjectTypeEnd,
214 | ciObjectVisibility,
215 | ciOldFormalParameterType,
216 | ciOrdinalIdentifier,
217 | ciOrdinalType,
218 | ciOutParameter,
219 | ciPackageFile,
220 | ciParameterFormal,
221 | ciParameterName,
222 | ciParameterNameList,
223 | ciParseFile,
224 | ciPointerType,
225 | ciProceduralDirective,
226 | ciProceduralType,
227 | ciProcedureDeclarationSection,
228 | ciProcedureHeading,
229 | ciProcedureMethodDeclaration,
230 | ciProcedureMethodName,
231 | ciProgramBlock,
232 | ciProgramFile,
233 | ciPropertyDefault,
234 | ciPropertyInterface,
235 | ciPropertyName,
236 | ciPropertyParameterConst,
237 | ciPropertyParameterList,
238 | ciPropertySpecifiers,
239 | ciQualifiedIdentifier,
240 | ciQualifiedIdentifierList,
241 | ciRaiseStatement,
242 | ciReadAccessIdentifier,
243 | ciRealIdentifier,
244 | ciRealType,
245 | ciRecordConstant,
246 | ciRecordFieldConstant,
247 | ciRecordType,
248 | ciRecordVariant,
249 | ciRelativeOperator,
250 | ciRepeatStatement,
251 | ciRequiresClause,
252 | ciRequiresIdentifier,
253 | ciResolutionInterfaceName,
254 | ciResourceDeclaration,
255 | ciReturnType,
256 | ciSEMICOLON,
257 | ciSetConstructor,
258 | ciSetElement,
259 | ciSetType,
260 | ciSimpleExpression,
261 | ciSimpleStatement,
262 | ciSimpleType,
263 | ciSkipAnsiComment,
264 | ciSkipBorComment,
265 | ciSkipSlashesComment,
266 | ciSkipSpace,
267 | ciSkipCRLFco,
268 | ciSkipCRLF,
269 | ciStatement,
270 | ciStatementList,
271 | ciStorageExpression,
272 | ciStorageIdentifier,
273 | ciStorageDefault,
274 | ciStorageNoDefault,
275 | ciStorageSpecifier,
276 | ciStorageStored,
277 | ciStringIdentifier,
278 | ciStringStatement,
279 | ciStringType,
280 | ciStructuredType,
281 | ciSubrangeType,
282 | ciTagField,
283 | ciTagFieldName,
284 | ciTagFieldTypeName,
285 | ciTerm,
286 | ciTryStatement,
287 | ciTypedConstant,
288 | ciTypeDeclaration,
289 | ciTypeId,
290 | ciTypeKind,
291 | ciTypeName,
292 | ciTypeSection,
293 | ciUnitFile,
294 | ciUnitId,
295 | ciUsedUnitName,
296 | ciUsedUnitsList,
297 | ciUsesClause,
298 | ciVarAbsolute,
299 | ciVarEqual,
300 | ciVarDeclaration,
301 | ciVariable,
302 | ciVariableList,
303 | ciVariableReference,
304 | ciVariableTwo,
305 | ciVariantIdentifier,
306 | ciVariantSection,
307 | ciVarParameter,
308 | ciVarSection,
309 | ciVisibilityAutomated,
310 | ciVisibilityPrivate,
311 | ciVisibilityProtected,
312 | ciVisibilityPublic,
313 | ciVisibilityPublished,
314 | ciVisibilityUnknown,
315 | ciWhileStatement,
316 | ciWithStatement,
317 | ciWriteAccessIdentifier
318 | );
319 |
320 | function ParserErrorName(Value: TmwParseError): string;
321 |
322 | implementation
323 |
324 | function ParserErrorName(Value: TmwParseError): string;
325 | begin
326 | result := GetEnumName(TypeInfo(TmwParseError), Integer(Value));
327 | end;
328 |
329 | end.
330 |
331 |
--------------------------------------------------------------------------------
/Source/DelphiAST/SimpleParser.inc:
--------------------------------------------------------------------------------
1 | {$IFDEF VER350} // Delphi 11
2 | {$DEFINE D8_NEWER}
3 | {$DEFINE D9_NEWER}
4 | {$DEFINE D10_NEWER}
5 | {$DEFINE D11_NEWER}
6 | {$DEFINE D12_NEWER}
7 | {$DEFINE D14_NEWER}
8 | {$DEFINE D15_NEWER}
9 | {$DEFINE D16_NEWER}
10 | {$DEFINE D17_NEWER}
11 | {$DEFINE D18_NEWER}
12 | {$DEFINE D19_NEWER}
13 | {$DEFINE D20_NEWER}
14 | {$DEFINE D21_NEWER}
15 | {$DEFINE D22_NEWER}
16 | {$DEFINE D23_NEWER}
17 | {$DEFINE D24_NEWER}
18 | {$DEFINE D25_NEWER}
19 | {$DEFINE D26_NEWER}
20 | {$DEFINE D27_NEWER}
21 | {$ENDIF}
22 |
23 | {$IFDEF VER340} // Delphi 10.4
24 | {$DEFINE D8_NEWER}
25 | {$DEFINE D9_NEWER}
26 | {$DEFINE D10_NEWER}
27 | {$DEFINE D11_NEWER}
28 | {$DEFINE D12_NEWER}
29 | {$DEFINE D14_NEWER}
30 | {$DEFINE D15_NEWER}
31 | {$DEFINE D16_NEWER}
32 | {$DEFINE D17_NEWER}
33 | {$DEFINE D18_NEWER}
34 | {$DEFINE D19_NEWER}
35 | {$DEFINE D20_NEWER}
36 | {$DEFINE D21_NEWER}
37 | {$DEFINE D22_NEWER}
38 | {$DEFINE D23_NEWER}
39 | {$DEFINE D24_NEWER}
40 | {$DEFINE D25_NEWER}
41 | {$DEFINE D26_NEWER}
42 | {$ENDIF}
43 |
44 | {$IFDEF VER330} // Delphi 10.3
45 | {$DEFINE D8_NEWER}
46 | {$DEFINE D9_NEWER}
47 | {$DEFINE D10_NEWER}
48 | {$DEFINE D11_NEWER}
49 | {$DEFINE D12_NEWER}
50 | {$DEFINE D14_NEWER}
51 | {$DEFINE D15_NEWER}
52 | {$DEFINE D16_NEWER}
53 | {$DEFINE D17_NEWER}
54 | {$DEFINE D18_NEWER}
55 | {$DEFINE D19_NEWER}
56 | {$DEFINE D20_NEWER}
57 | {$DEFINE D21_NEWER}
58 | {$DEFINE D22_NEWER}
59 | {$DEFINE D23_NEWER}
60 | {$DEFINE D24_NEWER}
61 | {$DEFINE D25_NEWER}
62 | {$DEFINE D26_NEWER}
63 | {$ENDIF}
64 |
65 | {$IFDEF VER320} // Delphi 10 Tokyo
66 | {$DEFINE D8_NEWER}
67 | {$DEFINE D9_NEWER}
68 | {$DEFINE D10_NEWER}
69 | {$DEFINE D11_NEWER}
70 | {$DEFINE D12_NEWER}
71 | {$DEFINE D14_NEWER}
72 | {$DEFINE D15_NEWER}
73 | {$DEFINE D16_NEWER}
74 | {$DEFINE D17_NEWER}
75 | {$DEFINE D18_NEWER}
76 | {$DEFINE D19_NEWER}
77 | {$DEFINE D20_NEWER}
78 | {$DEFINE D21_NEWER}
79 | {$DEFINE D22_NEWER}
80 | {$DEFINE D23_NEWER}
81 | {$DEFINE D24_NEWER}
82 | {$DEFINE D25_NEWER}
83 | {$ENDIF}
84 |
85 | {$IFDEF VER310} // Delphi 10 Berlin
86 | {$DEFINE D8_NEWER}
87 | {$DEFINE D9_NEWER}
88 | {$DEFINE D10_NEWER}
89 | {$DEFINE D11_NEWER}
90 | {$DEFINE D12_NEWER}
91 | {$DEFINE D14_NEWER}
92 | {$DEFINE D15_NEWER}
93 | {$DEFINE D16_NEWER}
94 | {$DEFINE D17_NEWER}
95 | {$DEFINE D18_NEWER}
96 | {$DEFINE D19_NEWER}
97 | {$DEFINE D20_NEWER}
98 | {$DEFINE D21_NEWER}
99 | {$DEFINE D22_NEWER}
100 | {$DEFINE D23_NEWER}
101 | {$DEFINE D24_NEWER}
102 | {$ENDIF}
103 |
104 | {$IFDEF VER300} // Delphi 10 Seattle
105 | {$DEFINE D8_NEWER}
106 | {$DEFINE D9_NEWER}
107 | {$DEFINE D10_NEWER}
108 | {$DEFINE D11_NEWER}
109 | {$DEFINE D12_NEWER}
110 | {$DEFINE D14_NEWER}
111 | {$DEFINE D15_NEWER}
112 | {$DEFINE D16_NEWER}
113 | {$DEFINE D17_NEWER}
114 | {$DEFINE D18_NEWER}
115 | {$DEFINE D19_NEWER}
116 | {$DEFINE D20_NEWER}
117 | {$DEFINE D21_NEWER}
118 | {$DEFINE D22_NEWER}
119 | {$DEFINE D23_NEWER}
120 | {$ENDIF}
121 |
122 | {$IFDEF VER290} // Delphi XE8
123 | {$DEFINE D8_NEWER}
124 | {$DEFINE D9_NEWER}
125 | {$DEFINE D10_NEWER}
126 | {$DEFINE D11_NEWER}
127 | {$DEFINE D12_NEWER}
128 | {$DEFINE D14_NEWER}
129 | {$DEFINE D15_NEWER}
130 | {$DEFINE D16_NEWER}
131 | {$DEFINE D17_NEWER}
132 | {$DEFINE D18_NEWER}
133 | {$DEFINE D19_NEWER}
134 | {$DEFINE D20_NEWER}
135 | {$DEFINE D21_NEWER}
136 | {$DEFINE D22_NEWER}
137 | {$ENDIF}
138 |
139 | {$IFDEF VER280} // Delphi XE7
140 | {$DEFINE D8_NEWER}
141 | {$DEFINE D9_NEWER}
142 | {$DEFINE D10_NEWER}
143 | {$DEFINE D11_NEWER}
144 | {$DEFINE D12_NEWER}
145 | {$DEFINE D14_NEWER}
146 | {$DEFINE D15_NEWER}
147 | {$DEFINE D16_NEWER}
148 | {$DEFINE D17_NEWER}
149 | {$DEFINE D18_NEWER}
150 | {$DEFINE D19_NEWER}
151 | {$DEFINE D20_NEWER}
152 | {$DEFINE D21_NEWER}
153 | {$ENDIF}
154 |
155 | {$IFDEF VER270} // Delphi XE6
156 | {$DEFINE D8_NEWER}
157 | {$DEFINE D9_NEWER}
158 | {$DEFINE D10_NEWER}
159 | {$DEFINE D11_NEWER}
160 | {$DEFINE D12_NEWER}
161 | {$DEFINE D14_NEWER}
162 | {$DEFINE D15_NEWER}
163 | {$DEFINE D16_NEWER}
164 | {$DEFINE D17_NEWER}
165 | {$DEFINE D18_NEWER}
166 | {$DEFINE D19_NEWER}
167 | {$DEFINE D20_NEWER}
168 | {$ENDIF}
169 |
170 | {$IFDEF VER260} // Delphi XE5
171 | {$DEFINE D8_NEWER}
172 | {$DEFINE D9_NEWER}
173 | {$DEFINE D10_NEWER}
174 | {$DEFINE D11_NEWER}
175 | {$DEFINE D12_NEWER}
176 | {$DEFINE D14_NEWER}
177 | {$DEFINE D15_NEWER}
178 | {$DEFINE D16_NEWER}
179 | {$DEFINE D17_NEWER}
180 | {$DEFINE D18_NEWER}
181 | {$DEFINE D19_NEWER}
182 | {$ENDIF}
183 |
184 | {$IFDEF VER250} // Delphi XE4
185 | {$DEFINE D8_NEWER}
186 | {$DEFINE D9_NEWER}
187 | {$DEFINE D10_NEWER}
188 | {$DEFINE D11_NEWER}
189 | {$DEFINE D12_NEWER}
190 | {$DEFINE D14_NEWER}
191 | {$DEFINE D15_NEWER}
192 | {$DEFINE D16_NEWER}
193 | {$DEFINE D17_NEWER}
194 | {$DEFINE D18_NEWER}
195 | {$ENDIF}
196 |
197 | {$IFDEF VER240} // Delphi XE3
198 | {$DEFINE D8_NEWER}
199 | {$DEFINE D9_NEWER}
200 | {$DEFINE D10_NEWER}
201 | {$DEFINE D11_NEWER}
202 | {$DEFINE D12_NEWER}
203 | {$DEFINE D14_NEWER}
204 | {$DEFINE D15_NEWER}
205 | {$DEFINE D16_NEWER}
206 | {$DEFINE D17_NEWER}
207 | {$ENDIF}
208 |
209 | {$IFDEF VER230} // Delphi XE2
210 | {$DEFINE D8_NEWER}
211 | {$DEFINE D9_NEWER}
212 | {$DEFINE D10_NEWER}
213 | {$DEFINE D11_NEWER}
214 | {$DEFINE D12_NEWER}
215 | {$DEFINE D14_NEWER}
216 | {$DEFINE D15_NEWER}
217 | {$DEFINE D16_NEWER}
218 | {$ENDIF}
219 |
220 | {$IFDEF VER220} // Delphi XE
221 | {$DEFINE D8_NEWER}
222 | {$DEFINE D9_NEWER}
223 | {$DEFINE D10_NEWER}
224 | {$DEFINE D11_NEWER}
225 | {$DEFINE D12_NEWER}
226 | {$DEFINE D14_NEWER}
227 | {$DEFINE D15_NEWER}
228 | {$ENDIF}
229 |
230 | {$IFDEF VER210} // Delphi 2010
231 | {$DEFINE D8_NEWER}
232 | {$DEFINE D9_NEWER}
233 | {$DEFINE D10_NEWER}
234 | {$DEFINE D11_NEWER}
235 | {$DEFINE D12_NEWER}
236 | {$DEFINE D14_NEWER}
237 | {$ENDIF}
238 |
239 | {$IFDEF VER200} // Delphi 2009
240 | {$DEFINE D8_NEWER}
241 | {$DEFINE D9_NEWER}
242 | {$DEFINE D10_NEWER}
243 | {$DEFINE D11_NEWER}
244 | {$DEFINE D12_NEWER}
245 | {$ENDIF}
246 |
247 | {$IFDEF VER190} // Delphi 2007 .NET
248 | {$DEFINE D8_NEWER}
249 | {$DEFINE D9_NEWER}
250 | {$DEFINE D10_NEWER}
251 | {$DEFINE D11_NEWER}
252 | {$ENDIF}
253 |
254 | {$IFDEF VER185} // Delphi 2007
255 | {$DEFINE D8_NEWER}
256 | {$DEFINE D9_NEWER}
257 | {$DEFINE D10_NEWER}
258 | {$ENDIF}
259 |
260 | {$IFDEF VER180} // Delphi 2006
261 | {$DEFINE D8_NEWER}
262 | {$DEFINE D9_NEWER}
263 | {$DEFINE D10_NEWER}
264 | {$ENDIF}
265 |
266 | {$IFDEF VER170} // Delphi 2005
267 | {$DEFINE D8_NEWER}
268 | {$DEFINE D9_NEWER}
269 | {$ENDIF}
270 |
271 | {$IFDEF VER160} // Delphi 8
272 | {$DEFINE D8_NEWER}
273 | {$ENDIF}
274 |
275 | {$IFDEF D18_NEWER}
276 | {$DEFINE SUPPORTS_INTRINSIC_HELPERS}
277 | {$ENDIF}
278 |
279 | {$IFNDEF D16_NEWER}
280 | {$DEFINE CPUX86}
281 | {$ENDIF}
282 |
--------------------------------------------------------------------------------
/Source/HighDPI/CodeCoverage.Images.DM.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.Images.DM;
2 |
3 | interface
4 |
5 | uses
6 | Winapi.Windows,
7 | System.ImageList, System.Classes, System.Actions, System.Messaging,
8 | Vcl.ImgList, Vcl.Controls, Vcl.Menus, Vcl.ActnList, Vcl.Graphics, Vcl.VirtualImageList, Vcl.BaseImageCollection,
9 | Vcl.ImageCollection, Vcl.Forms;
10 |
11 | type
12 | TdmCodeCoverageImages = class(TDatamodule)
13 | Images: TVirtualImageList;
14 | MainImageCollection: TImageCollection;
15 | private
16 | FDPIChangedMessageID: Integer;
17 | procedure DPIChangedMessageHandler(const Sender: TObject; const M: System.Messaging.TMessage);
18 | function GetImageArray(const AImageName: string): TGraphicArray;
19 | public
20 | constructor Create(AOwner: TComponent); override;
21 | destructor Destroy; override;
22 | function FindImageIndexByName(const AImageName: string): Integer;
23 | property ImageArray[const AImageName: string]: TGraphicArray read GetImageArray;
24 | end;
25 |
26 | var
27 | dmCodeCoverageImages: TdmCodeCoverageImages;
28 |
29 | implementation
30 |
31 | uses
32 | CodeCoverage.ApiHelper;
33 |
34 | {%CLASSGROUP 'Vcl.Controls.TControl'}
35 |
36 | {$R *.dfm}
37 |
38 | constructor TdmCodeCoverageImages.Create(AOwner: TComponent);
39 | begin
40 | inherited;
41 | FDPIChangedMessageID := TMessageManager.DefaultManager.SubscribeToMessage(TChangeScaleMessage, DPIChangedMessageHandler);
42 | end;
43 |
44 | destructor TdmCodeCoverageImages.Destroy;
45 | begin
46 | TMessageManager.DefaultManager.Unsubscribe(TChangeScaleMessage, FDPIChangedMessageID);
47 | inherited;
48 | end;
49 |
50 | procedure TdmCodeCoverageImages.DPIChangedMessageHandler(const Sender: TObject; const M: System.Messaging.TMessage);
51 | var
52 | size: Integer;
53 | begin
54 | size := NTA.Services.ImageList.Width;
55 | Images.SetSize(size, size);
56 | end;
57 |
58 | function TdmCodeCoverageImages.FindImageIndexByName(const AImageName: string): Integer;
59 | begin
60 | Result := Images.GetIndexByName(AImageName);
61 | end;
62 |
63 | function TdmCodeCoverageImages.GetImageArray(const AImageName: string): TGraphicArray;
64 | var
65 | idx: Integer;
66 | item: TImageCollectionItem;
67 | I: Integer;
68 | begin
69 | idx := MainImageCollection.GetIndexByName(AImageName);
70 | if idx < 0 then
71 | Exit(nil);
72 | item := MainImageCollection.Images[idx];
73 | SetLength(Result, item.SourceImages.Count);
74 | for I := 0 to item.SourceImages.Count - 1 do
75 | Result[I] := item.SourceImages[I].Image;
76 | end;
77 |
78 | end.
79 |
80 |
--------------------------------------------------------------------------------
/Source/LowDPI/CodeCoverage.Images.DM.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.Images.DM;
2 |
3 | interface
4 |
5 | uses
6 | System.ImageList, System.Classes, System.Actions,
7 | Vcl.ImgList, Vcl.Controls, Vcl.Menus, Vcl.ActnList, Vcl.Graphics,
8 | PngImageList;
9 |
10 | type
11 | TdmCodeCoverageImages = class(TDatamodule)
12 | Images: TPngImageList;
13 | public
14 | function FindImageIndexByName(const AImageName: string): Integer;
15 | end;
16 |
17 | var
18 | dmCodeCoverageImages: TdmCodeCoverageImages;
19 |
20 | implementation
21 |
22 | {%CLASSGROUP 'Vcl.Controls.TControl'}
23 |
24 | {$R *.dfm}
25 |
26 | function TdmCodeCoverageImages.FindImageIndexByName(const AImageName: string): Integer;
27 | begin
28 | Result := Images.FindIndexByName(AImageName);
29 | end;
30 |
31 | end.
32 |
33 |
--------------------------------------------------------------------------------
/Source/PngComponents/CodeCoverage.PngFunctions.pas:
--------------------------------------------------------------------------------
1 | unit CodeCoverage.PngFunctions;
2 |
3 | interface
4 |
5 | uses
6 | Windows, Graphics, ImgList, Contnrs, pngimage;
7 |
8 | {$IF RTLVersion < 20.0 }
9 | {$IF RTLVersion < 15.0 }
10 | PngComponents are only compatible with Delphi 7 and higher!
11 | {$IFEND}
12 | type
13 | TPngImage = TPNGObject;
14 | {$IFEND}
15 |
16 | type
17 | TPngOption = (pngBlendOnDisabled, pngGrayscaleOnDisabled);
18 | TPngOptions = set of TPngOption;
19 |
20 | procedure MakeDisabledImage(Image: TPngImage; const Options: TPngOptions);
21 | procedure MakeImageBlended(Image: TPngImage; Amount: Byte = 127);
22 | procedure MakeImageGrayscale(Image: TPngImage; Amount: Byte = 255);
23 | procedure DrawPNG(Png: TPngImage; Canvas: TCanvas; const ARect: TRect; const Options: TPngOptions);
24 | procedure ConvertToPNG(Source: TGraphic; Dest: TPngImage);
25 | procedure CreatePNG(Color, Mask: TBitmap; Dest: TPngImage; InverseMask: Boolean = False);
26 | procedure CreatePNGMasked(Bitmap: TBitmap; Mask: TColor; Dest: TPngImage);
27 | procedure SlicePNG(JoinedPNG: TPngImage; Columns, Rows: Integer; out SlicedPNGs: TObjectList);
28 |
29 | implementation
30 |
31 | uses
32 | SysUtils;
33 |
34 | function ColorToTriple(Color: TColor): TRGBTriple;
35 | var
36 | ColorRGB: Longint;
37 | begin
38 | ColorRGB := ColorToRGB(Color);
39 | Result.rgbtBlue := ColorRGB shr 16 and $FF;
40 | Result.rgbtGreen := ColorRGB shr 8 and $FF;
41 | Result.rgbtRed := ColorRGB and $FF;
42 | end;
43 |
44 | procedure MakeImageBlended(Image: TPngImage; Amount: Byte = 127);
45 |
46 | procedure ForceAlphachannel(BitTransparency: Boolean; TransparentColor: TColor);
47 | var
48 | Assigner: TBitmap;
49 | Temp: TPngImage;
50 | X, Y: Integer;
51 | Line: pngimage.PByteArray;
52 | Current: TColor;
53 | begin
54 | //Not all formats of PNG support an alpha-channel (paletted images for example),
55 | //so with this function, I simply recreate the PNG as being 32-bits, effectivly
56 | //forcing an alpha-channel on it.
57 | Temp := TPngImage.Create;
58 | try
59 | Assigner := TBitmap.Create;
60 | try
61 | Assigner.Width := Image.Width;
62 | Assigner.Height := Image.Height;
63 | Temp.Assign(Assigner);
64 | finally
65 | Assigner.Free;
66 | end;
67 | Temp.CreateAlpha;
68 | for Y := 0 to Image.Height - 1 do begin
69 | Line := Temp.AlphaScanline[Y];
70 | for X := 0 to Image.Width - 1 do begin
71 | Current := Image.Pixels[X, Y];
72 | Temp.Pixels[X, Y] := Current;
73 | if BitTransparency and (Current = TransparentColor) then
74 | Line[X] := 0
75 | else
76 | Line[X] := Amount;
77 | end;
78 | end;
79 | Image.Assign(Temp);
80 | finally
81 | Temp.Free;
82 | end;
83 | end;
84 |
85 | var
86 | X, Y: Integer;
87 | Line: pngimage.PByteArray;
88 | Forced: Boolean;
89 | TransparentColor: TColor;
90 | BitTransparency: Boolean;
91 | begin
92 | //If the PNG doesn't have an alpha channel, then add one
93 | BitTransparency := Image.TransparencyMode = ptmBit;
94 | TransparentColor := Image.TransparentColor;
95 | Forced := False;
96 | if not (Image.Header.ColorType in [COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA]) then begin
97 | Forced := Image.Header.ColorType in [COLOR_GRAYSCALE, COLOR_PALETTE];
98 | if Forced then
99 | ForceAlphachannel(BitTransparency, TransparentColor)
100 | else
101 | Image.CreateAlpha;
102 | end;
103 |
104 | //Divide the alpha values by 2
105 | if not Forced and (Image.Header.ColorType in [COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA]) then begin
106 | for Y := 0 to Image.Height - 1 do begin
107 | Line := Image.AlphaScanline[Y];
108 | for X := 0 to Image.Width - 1 do begin
109 | if BitTransparency and (Image.Pixels[X, Y] = TransparentColor) then
110 | Line[X] := 0
111 | else
112 | Line[X] := Round(Line[X] / 256 * (Amount + 1));
113 | end;
114 | end;
115 | end;
116 | end;
117 |
118 | procedure MakeImageGrayscale(Image: TPngImage; Amount: Byte = 255);
119 |
120 | procedure GrayscaleRGB(var R, G, B: Byte);
121 | { Performance optimized version without floating point operations by Christian Budde }
122 | var
123 | X: Byte;
124 | begin
125 | X := (R * 77 + G * 150 + B * 29) shr 8;
126 | R := ((R * (255 - Amount)) + (X * Amount) + 128) shr 8;
127 | G := ((G * (255 - Amount)) + (X * Amount) + 128) shr 8;
128 | B := ((B * (255 - Amount)) + (X * Amount) + 128) shr 8;
129 | (* original code
130 | X := Round(R * 0.30 + G * 0.59 + B * 0.11);
131 | R := Round(R / 256 * (256 - Amount - 1)) + Round(X / 256 * (Amount + 1));
132 | G := Round(G / 256 * (256 - Amount - 1)) + Round(X / 256 * (Amount + 1));
133 | B := Round(B / 256 * (256 - Amount - 1)) + Round(X / 256 * (Amount + 1));
134 | *)
135 | end;
136 |
137 | var
138 | X, Y, PalCount: Integer;
139 | Line: PRGBLine;
140 | PaletteHandle: HPalette;
141 | Palette: array[Byte] of TPaletteEntry;
142 | begin
143 | //Don't do anything if the image is already a grayscaled one
144 | if not (Image.Header.ColorType in [COLOR_GRAYSCALE, COLOR_GRAYSCALEALPHA]) then begin
145 | if Image.Header.ColorType = COLOR_PALETTE then begin
146 | //Grayscale every palette entry
147 | PaletteHandle := Image.Palette;
148 | PalCount := GetPaletteEntries(PaletteHandle, 0, 256, Palette);
149 | for X := 0 to PalCount - 1 do
150 | GrayscaleRGB(Palette[X].peRed, Palette[X].peGreen, Palette[X].peBlue);
151 | SetPaletteEntries(PaletteHandle, 0, PalCount, Palette);
152 | Image.Palette := PaletteHandle;
153 | end
154 | else begin
155 | //Grayscale every pixel
156 | for Y := 0 to Image.Height - 1 do begin
157 | Line := Image.Scanline[Y];
158 | for X := 0 to Image.Width - 1 do
159 | GrayscaleRGB(Line[X].rgbtRed, Line[X].rgbtGreen, Line[X].rgbtBlue);
160 | end;
161 | end;
162 | end;
163 | end;
164 |
165 | procedure DrawPNG(Png: TPngImage; Canvas: TCanvas; const ARect: TRect; const Options: TPngOptions);
166 | var
167 | PngCopy: TPngImage;
168 | begin
169 | if Options <> [] then begin
170 | PngCopy := TPngImage.Create;
171 | try
172 | PngCopy.Assign(Png);
173 | if pngBlendOnDisabled in Options then
174 | MakeImageBlended(PngCopy);
175 | if pngGrayscaleOnDisabled in Options then
176 | MakeImageGrayscale(PngCopy);
177 | PngCopy.Draw(Canvas, ARect);
178 | finally
179 | PngCopy.Free;
180 | end;
181 | end
182 | else begin
183 | Png.Draw(Canvas, ARect);
184 | end;
185 | end;
186 |
187 | procedure ConvertToPNG(Source: TGraphic; Dest: TPngImage);
188 | type
189 | TRGBALine = array[Word] of TRGBQuad;
190 | PRGBALine = ^TRGBALine;
191 | var
192 | MaskLines: array of pngimage.PByteArray;
193 |
194 | function ColorToTriple(const Color: TColor): TRGBTriple;
195 | begin
196 | Result.rgbtBlue := Color shr 16 and $FF;
197 | Result.rgbtGreen := Color shr 8 and $FF;
198 | Result.rgbtRed := Color and $FF;
199 | end;
200 |
201 | procedure GetAlphaMask(SourceColor: TBitmap);
202 | type
203 | TBitmapInfoV4 = packed record
204 | bmiHeader: TBitmapV4Header; //Otherwise I may not get per-pixel alpha values.
205 | bmiColors: array[0..2] of TRGBQuad; // reserve space for color lookup table
206 | end;
207 | var
208 | Bits: PRGBALine;
209 | { The BitmapInfo parameter to GetDIBits is delared as var parameter. So instead of casting around, we simply use
210 | the absolute directive to refer to the same memory area. }
211 | BitmapInfo: TBitmapInfoV4;
212 | BitmapInfoFake: TBitmapInfo absolute BitmapInfo;
213 | I, X, Y: Integer;
214 | HasAlpha: Boolean;
215 | BitsSize: Integer;
216 | bmpDC: HDC;
217 | bmpHandle: HBITMAP;
218 | begin
219 | BitsSize := 4 * SourceColor.Width * SourceColor.Height;
220 | Bits := AllocMem(BitsSize);
221 | try
222 | FillChar(BitmapInfo, SizeOf(BitmapInfo), 0);
223 | BitmapInfo.bmiHeader.bV4Size := SizeOf(BitmapInfo.bmiHeader);
224 | BitmapInfo.bmiHeader.bV4Width := SourceColor.Width;
225 | BitmapInfo.bmiHeader.bV4Height := -SourceColor.Height; //Otherwise the image is upside down.
226 | BitmapInfo.bmiHeader.bV4Planes := 1;
227 | BitmapInfo.bmiHeader.bV4BitCount := 32;
228 | BitmapInfo.bmiHeader.bV4V4Compression := BI_BITFIELDS;
229 | BitmapInfo.bmiHeader.bV4SizeImage := BitsSize;
230 | BitmapInfo.bmiColors[0].rgbRed := 255;
231 | BitmapInfo.bmiColors[1].rgbGreen := 255;
232 | BitmapInfo.bmiColors[2].rgbBlue := 255;
233 |
234 | { Getting the bitmap Handle will invalidate the Canvas.Handle, so it is important to retrieve them in the correct
235 | order. As parameter evaluation order is undefined and differs between Win32 and Win64, we get invalid values
236 | for Canvas.Handle when we use those properties directly in the call to GetDIBits. }
237 | bmpHandle := SourceColor.Handle;
238 | bmpDC := SourceColor.Canvas.Handle;
239 | if GetDIBits(bmpDC, bmpHandle, 0, SourceColor.Height, Bits, BitmapInfoFake, DIB_RGB_COLORS) > 0 then begin
240 | //Because Win32 API is a piece of crap when it comes to icons, I have to check
241 | //whether an has an alpha-channel the hard way.
242 | HasAlpha := False;
243 | for I := 0 to (SourceColor.Height * SourceColor.Width) - 1 do begin
244 | if Bits[I].rgbReserved <> 0 then begin
245 | HasAlpha := True;
246 | Break;
247 | end;
248 | end;
249 | if HasAlpha then begin
250 | //OK, so not all alpha-values are 0, which indicates the existence of an
251 | //alpha-channel.
252 | I := 0;
253 | for Y := 0 to SourceColor.Height - 1 do
254 | for X := 0 to SourceColor.Width - 1 do begin
255 | MaskLines[Y][X] := Bits[I].rgbReserved;
256 | Inc(I);
257 | end;
258 | end;
259 | end;
260 | finally
261 | FreeMem(Bits, BitsSize);
262 | end;
263 | end;
264 |
265 | function WinXPOrHigher: Boolean;
266 | var
267 | Info: TOSVersionInfo;
268 | begin
269 | Info.dwOSVersionInfoSize := SizeOf(Info);
270 | GetVersionEx(Info);
271 | Result := (Info.dwPlatformId = VER_PLATFORM_WIN32_NT) and
272 | ((Info.dwMajorVersion > 5) or
273 | ((Info.dwMajorVersion = 5) and (Info.dwMinorVersion >= 1)));
274 | end;
275 |
276 | var
277 | Temp, SourceColor, SourceMask: TBitmap;
278 | X, Y: Integer;
279 | Line: PRGBLine;
280 | MaskLine, AlphaLine: pngimage.PByteArray;
281 | TransparentColor, CurrentColor: TColor;
282 | IconInfo: TIconInfo;
283 | AlphaNeeded: Boolean;
284 | begin
285 | Assert(Dest <> nil, 'Dest is nil!');
286 | //A PNG does not have to be converted
287 | if Source is TPngImage then begin
288 | Dest.Assign(Source);
289 | Exit;
290 | end;
291 |
292 | AlphaNeeded := False;
293 | Temp := TBitmap.Create;
294 | SetLength(MaskLines, Source.Height);
295 | for Y := 0 to Source.Height - 1 do begin
296 | MaskLines[Y] := AllocMem(Source.Width);
297 | FillMemory(MaskLines[Y], Source.Width, 255);
298 | end;
299 | try
300 | //Initialize intermediate color bitmap
301 | Temp.Width := Source.Width;
302 | Temp.Height := Source.Height;
303 | Temp.PixelFormat := pf24bit;
304 |
305 | //Now figure out the transparency
306 | if Source is TBitmap then begin
307 | if Source.Transparent then begin
308 | //TBitmap is just about comparing the drawn colors against the TransparentColor
309 | if TBitmap(Source).TransparentMode = tmFixed then
310 | TransparentColor := TBitmap(Source).TransparentColor
311 | else
312 | TransparentColor := TBitmap(Source).Canvas.Pixels[0, Source.Height - 1];
313 |
314 | for Y := 0 to Temp.Height - 1 do begin
315 | Line := Temp.ScanLine[Y];
316 | MaskLine := MaskLines[Y];
317 | for X := 0 to Temp.Width - 1 do begin
318 | CurrentColor := GetPixel(TBitmap(Source).Canvas.Handle, X, Y);
319 | if CurrentColor = TransparentColor then begin
320 | MaskLine^[X] := 0;
321 | AlphaNeeded := True;
322 | end;
323 | Line[X] := ColorToTriple(CurrentColor);
324 | end;
325 | end;
326 | end
327 | else begin
328 | Temp.Canvas.Draw(0, 0, Source);
329 | end;
330 | end
331 | else if Source is TIcon then begin
332 | //TIcon is more complicated, because there are bitmasked (classic) icons and
333 | //alphablended (modern) icons. Not to forget about the "inverse" color.
334 | GetIconInfo(TIcon(Source).Handle, IconInfo);
335 | SourceColor := TBitmap.Create;
336 | SourceMask := TBitmap.Create;
337 | try
338 | SourceColor.Handle := IconInfo.hbmColor;
339 | SourceMask.Handle := IconInfo.hbmMask;
340 | Temp.Canvas.Draw(0, 0, SourceColor);
341 | for Y := 0 to Temp.Height - 1 do begin
342 | MaskLine := MaskLines[Y];
343 | for X := 0 to Temp.Width - 1 do begin
344 | if GetPixel(SourceMask.Canvas.Handle, X, Y) <> 0 then begin
345 | MaskLine^[X] := 0;
346 | AlphaNeeded := True;
347 | end;
348 | end;
349 | end;
350 | if (GetDeviceCaps(SourceColor.Canvas.Handle, BITSPIXEL) = 32) and WinXPOrHigher then begin
351 | //This doesn't neccesarily mean we actually have 32bpp in the icon, because the
352 | //bpp of an icon is always the same as the display settings, regardless of the
353 | //actual color depth of the icon :(
354 | AlphaNeeded := True;
355 | GetAlphaMask(SourceColor);
356 | end;
357 | //This still doesn't work for alphablended icons...
358 | finally
359 | SourceColor.Free;
360 | SourceMask.Free
361 | end;
362 | end;
363 |
364 | //And finally, assign the destination PNG image
365 | Dest.Assign(Temp);
366 | if AlphaNeeded then begin
367 | Dest.CreateAlpha;
368 | for Y := 0 to Dest.Height - 1 do begin
369 | AlphaLine := Dest.AlphaScanline[Y];
370 | CopyMemory(AlphaLine, MaskLines[Y], Temp.Width);
371 | end;
372 | end;
373 |
374 | finally
375 | for Y := 0 to Source.Height - 1 do
376 | FreeMem(MaskLines[Y], Source.Width);
377 | Temp.Free;
378 | end;
379 | end;
380 |
381 | procedure CreatePNG(Color, Mask: TBitmap; Dest: TPngImage; InverseMask: Boolean = False);
382 | var
383 | Temp: TBitmap;
384 | Line: pngimage.PByteArray;
385 | X, Y: Integer;
386 | begin
387 | Assert(Dest <> nil, 'Dest is nil!');
388 | //Create a PNG from two separate color and mask bitmaps. InverseMask should be
389 | //True if white means transparent, and black means opaque.
390 | if not (Color.PixelFormat in [pf24bit, pf32bit]) then begin
391 | Temp := TBitmap.Create;
392 | try
393 | Temp.Assign(Color);
394 | Temp.PixelFormat := pf24bit;
395 | Dest.Assign(Temp);
396 | finally
397 | Temp.Free;
398 | end;
399 | end
400 | else begin
401 | Dest.Assign(Color);
402 | end;
403 |
404 | //Copy the alpha channel.
405 | Dest.CreateAlpha;
406 | for Y := 0 to Dest.Height - 1 do begin
407 | Line := Dest.AlphaScanline[Y];
408 | for X := 0 to Dest.Width - 1 do begin
409 | if InverseMask then
410 | Line[X] := 255 - (GetPixel(Mask.Canvas.Handle, X, Y) and $FF)
411 | else
412 | Line[X] := GetPixel(Mask.Canvas.Handle, X, Y) and $FF;
413 | end;
414 | end;
415 | end;
416 |
417 | procedure CreatePNGMasked(Bitmap: TBitmap; Mask: TColor; Dest: TPngImage);
418 | var
419 | Temp: TBitmap;
420 | Line: pngimage.PByteArray;
421 | X, Y: Integer;
422 | begin
423 | Assert(Dest <> nil, 'Dest is nil!');
424 | //Create a PNG from two separate color and mask bitmaps. InverseMask should be
425 | //True if white means transparent, and black means opaque.
426 | if not (Bitmap.PixelFormat in [pf24bit, pf32bit]) then begin
427 | Temp := TBitmap.Create;
428 | try
429 | Temp.Assign(Bitmap);
430 | Temp.PixelFormat := pf24bit;
431 | Dest.Assign(Temp);
432 | finally
433 | Temp.Free;
434 | end;
435 | end
436 | else begin
437 | Dest.Assign(Bitmap);
438 | end;
439 |
440 | //Copy the alpha channel.
441 | Dest.CreateAlpha;
442 | for Y := 0 to Dest.Height - 1 do begin
443 | Line := Dest.AlphaScanline[Y];
444 | for X := 0 to Dest.Width - 1 do
445 | Line[X] := Integer(TColor(GetPixel(Bitmap.Canvas.Handle, X, Y)) <> Mask) * $FF;
446 | end;
447 | end;
448 |
449 | procedure SlicePNG(JoinedPNG: TPngImage; Columns, Rows: Integer; out SlicedPNGs: TObjectList);
450 | var
451 | X, Y, ImageX, ImageY, OffsetX, OffsetY: Integer;
452 | Width, Height: Integer;
453 | Bitmap: TBitmap;
454 | BitmapLine: PRGBLine;
455 | AlphaLineA, AlphaLineB: pngimage.PByteArray;
456 | PNG: TPngImage;
457 | begin
458 | //This function slices a large PNG file (e.g. an image with all images for a
459 | //toolbar) into smaller, equally-sized pictures.
460 | SlicedPNGs := TObjectList.Create(False);
461 | Width := JoinedPNG.Width div Columns;
462 | Height := JoinedPNG.Height div Rows;
463 |
464 | //Loop through the columns and rows to create each individual image
465 | for ImageY := 0 to Rows - 1 do begin
466 | for ImageX := 0 to Columns - 1 do begin
467 | OffsetX := ImageX * Width;
468 | OffsetY := ImageY * Height;
469 | Bitmap := TBitmap.Create;
470 | try
471 | Bitmap.Width := Width;
472 | Bitmap.Height := Height;
473 | Bitmap.PixelFormat := pf24bit;
474 |
475 | //Copy the color information into a temporary bitmap. We can't use TPngImage.Draw
476 | //here, because that would combine the color and alpha values.
477 | for Y := 0 to Bitmap.Height - 1 do begin
478 | BitmapLine := Bitmap.Scanline[Y];
479 | for X := 0 to Bitmap.Width - 1 do
480 | BitmapLine[X] := ColorToTriple(JoinedPNG.Pixels[X + OffsetX, Y + OffsetY]);
481 | end;
482 |
483 | PNG := TPngImage.Create;
484 | PNG.Assign(Bitmap);
485 |
486 | if JoinedPNG.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA] then begin
487 | //Copy the alpha channel
488 | PNG.CreateAlpha;
489 | for Y := 0 to PNG.Height - 1 do begin
490 | AlphaLineA := JoinedPNG.AlphaScanline[Y + OffsetY];
491 | AlphaLineB := PNG.AlphaScanline[Y];
492 | for X := 0 to PNG.Width - 1 do
493 | AlphaLineB[X] := AlphaLineA[X + OffsetX];
494 | end;
495 | end;
496 |
497 | SlicedPNGs.Add(PNG);
498 | finally
499 | Bitmap.Free;
500 | end;
501 | end;
502 | end;
503 | end;
504 |
505 | procedure MakeDisabledImage(Image: TPngImage; const Options: TPngOptions);
506 | begin
507 | if pngBlendOnDisabled in Options then
508 | MakeImageBlended(Image);
509 | if pngGrayscaleOnDisabled in Options then
510 | MakeImageGrayscale(Image);
511 | end;
512 |
513 | {$IF RTLVersion >= 20.0 }
514 | {$IFDEF RegisterOldPngFormat}
515 | type
516 | TPNGObject = class(TPngImage);
517 | initialization
518 | TPicture.RegisterFileFormat('', '', TPNGObject);
519 | finalization
520 | TPicture.UnregisterGraphicClass(TPNGObject);
521 | {$IFEND}
522 | {$IFEND}
523 | end.
524 |
--------------------------------------------------------------------------------
/Source/TestProject.dpr:
--------------------------------------------------------------------------------
1 | program TestProject;
2 |
3 | {$IFNDEF TESTINSIGHT}
4 | {$APPTYPE CONSOLE}
5 | {$ENDIF}{$STRONGLINKTYPES ON}
6 | uses
7 | System.SysUtils,
8 | {$IFDEF TESTINSIGHT}
9 | TestInsight.DUnitX,
10 | {$ENDIF }
11 | DUnitX.Loggers.Console,
12 | DUnitX.Loggers.Xml.NUnit,
13 | DUnitX.TestFramework,
14 | TestUnit1 in 'TestUnit1.pas',
15 | TestedUnit1 in 'TestedUnit1.pas';
16 |
17 | var
18 | runner : ITestRunner;
19 | results : IRunResults;
20 | logger : ITestLogger;
21 | nunitLogger : ITestLogger;
22 | begin
23 | {$IFDEF TESTINSIGHT}
24 | TestInsight.DUnitX.RunRegisteredTests;
25 | exit;
26 | {$ENDIF}
27 | try
28 | //Check command line options, will exit if invalid
29 | TDUnitX.CheckCommandLine;
30 | //Create the test runner
31 | runner := TDUnitX.CreateRunner;
32 | //Tell the runner to use RTTI to find Fixtures
33 | runner.UseRTTI := True;
34 | //tell the runner how we will log things
35 | //Log to the console window
36 | logger := TDUnitXConsoleLogger.Create(true);
37 | runner.AddLogger(logger);
38 | //Generate an NUnit compatible XML File
39 | nunitLogger := TDUnitXXMLNUnitFileLogger.Create(TDUnitX.Options.XMLOutputFile);
40 | runner.AddLogger(nunitLogger);
41 | runner.FailsOnNoAsserts := False; //When true, Assertions must be made during tests;
42 |
43 | //Run tests
44 | results := runner.Execute;
45 | if not results.AllPassed then
46 | System.ExitCode := EXIT_ERRORS;
47 |
48 | {$IFNDEF CI}
49 | //We don't want this happening when running under CI.
50 | if TDUnitX.Options.ExitBehavior = TDUnitXExitBehavior.Pause then
51 | begin
52 | System.Write('Done.. press key to quit.');
53 | System.Readln;
54 | end;
55 | {$ENDIF}
56 | except
57 | on E: Exception do
58 | System.Writeln(E.ClassName, ': ', E.Message);
59 | end;
60 | end.
61 |
--------------------------------------------------------------------------------
/Source/TestUnit1.pas:
--------------------------------------------------------------------------------
1 | unit TestUnit1;
2 |
3 | interface
4 | uses
5 | DUnitX.TestFramework, TestedUnit1;
6 |
7 | type
8 |
9 | [TestFixture]
10 | TMyTestObject = class(TObject)
11 | private
12 | FInstance: TTestedClass;
13 | public
14 | [Setup]
15 | procedure Setup;
16 | [TearDown]
17 | procedure TearDown;
18 | [Test]
19 | [TestCase('Test1', '2,1,1')]
20 | // [TestCase('Test2', '1,2,1')]
21 | // [TestCase('Test3', '-2,1,1')]
22 | // [TestCase('Test4', '1,-2,1')]
23 | procedure TestMethod1(A, B, C: Integer);
24 | [Test]
25 | [TestCase('Test1a', '2,1,1')]
26 | // [TestCase('Test2a', '1,2,1')]
27 | // [TestCase('Test3a', '-2,1,1')]
28 | // [TestCase('Test4a', '1,-2,1')]
29 | procedure TestMethod2(A, B, C: Integer);
30 | end;
31 |
32 | implementation
33 |
34 | procedure TMyTestObject.Setup;
35 | begin
36 | FInstance := TTestedClass.Create;
37 | end;
38 |
39 | procedure TMyTestObject.TearDown;
40 | begin
41 | FInstance.Free;
42 | FInstance := nil;
43 | end;
44 |
45 |
46 | procedure TMyTestObject.TestMethod1(A, B, C: Integer);
47 | var
48 | res: Integer;
49 | begin
50 | FInstance.TestedMethod1(A, B, res);
51 | Assert.AreEqual(C, res);
52 | end;
53 |
54 | procedure TMyTestObject.TestMethod2(A, B, C: Integer);
55 | var
56 | res: Integer;
57 | begin
58 | FInstance.TestedMethod2(A, B, res);
59 | Assert.AreEqual(C, res);
60 | end;
61 |
62 | initialization
63 | TDUnitX.RegisterTestFixture(TMyTestObject);
64 | end.
65 |
--------------------------------------------------------------------------------
/Source/TestedUnit1.pas:
--------------------------------------------------------------------------------
1 | unit TestedUnit1;
2 |
3 | interface
4 |
5 | type
6 | TTestedClass = class
7 | public
8 | procedure TestedMethod1(A, B: Integer; out C: Integer);
9 | procedure TestedMethod2(A, B: Integer; out C: Integer);
10 | end;
11 |
12 | implementation
13 |
14 | procedure TTestedClass.TestedMethod1(A, B: Integer; out C: Integer);
15 | begin
16 | if A < 0 then begin
17 | A := -A;
18 | end;
19 |
20 | if B < 0 then
21 | begin
22 | B := -B;
23 | end;
24 |
25 | if B < A then begin
26 | C := A - B;
27 | end
28 | else begin
29 | C := B - A;
30 | end;
31 | end;
32 |
33 | procedure TTestedClass.TestedMethod2(A, B: Integer; out C: Integer);
34 | begin
35 | if A < 0 then begin
36 | A := -A;
37 | end;
38 |
39 | if B < 0 then
40 | begin
41 | B := -B;
42 | end;
43 |
44 | if B < A then begin
45 | C := A - B;
46 | end
47 | else begin
48 | C := B - A;
49 | end;
50 | end;
51 |
52 | end.
53 |
--------------------------------------------------------------------------------