├── .gitignore ├── DGVisualStudioCodeIntegration.pas ├── EditInVSCode.dpk ├── EditInVSCode.dproj ├── EditInVSCode.res ├── EditInVsCodeGrp.groupproj ├── LICENSE ├── OSCmdLineExecutor.pas └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.dsv 2 | *.bak 3 | *.skincfg 4 | 5 | # Delphi compiler-generated binaries (safe to delete) 6 | *.exe 7 | *.dll 8 | *.bpl 9 | *.bpi 10 | *.dcp 11 | *.so 12 | *.apk 13 | *.drc 14 | *.map 15 | *.dres 16 | *.rsm 17 | *.tds 18 | *.dcu 19 | *.lib 20 | *.a 21 | *.o 22 | *.ocx 23 | 24 | # Delphi local files (user-specific info) 25 | *.local 26 | *.identcache 27 | *.projdata 28 | *.tvsconfig 29 | *.dsk 30 | *.delphilsp.json 31 | .vscode/ 32 | 33 | # Delphi history and backups 34 | __history/ 35 | __recovery/ 36 | *.~* 37 | 38 | # Compiled Binary Folders 39 | Android64/ 40 | Linux64/ 41 | Win32/ 42 | Win64/ 43 | TMSWEB/ 44 | -------------------------------------------------------------------------------- /DGVisualStudioCodeIntegration.pas: -------------------------------------------------------------------------------- 1 | unit DGVisualStudioCodeIntegration; 2 | 3 | interface 4 | 5 | procedure Register; 6 | 7 | implementation 8 | uses 9 | System.Classes, 10 | System.SysUtils, 11 | System.Generics.Collections, 12 | ToolsAPI, 13 | Vcl.Menus, 14 | OSCmdLineExecutor, 15 | Vcl.Dialogs, 16 | Vcl.ActnList, 17 | Vcl.Forms, 18 | Winapi.Windows, 19 | WinApi.ShellAPI; 20 | 21 | 22 | 23 | // Returns true if the module was saved successfully (or it didn't need to be saved) 24 | function SaveModule(Module: IOTAModule): boolean; 25 | var 26 | i: Integer; 27 | editor: IOTAEditor; 28 | begin 29 | for i := 0 to Module.ModuleFileCount - 1 do begin 30 | editor := Module.ModuleFileEditors[i]; 31 | if not Editor.Modified then 32 | continue; 33 | exit(Module.Save(False, True)); 34 | end; 35 | exit(true); 36 | end; 37 | 38 | function SaveAllModules:boolean; 39 | var 40 | Services: IOTAModuleServices; 41 | I: Integer; 42 | Module: IOTAModule; 43 | begin 44 | result:=false; 45 | Services := BorlandIDEServices as IOTAModuleServices; 46 | for I := 0 to Services.ModuleCount - 1 do begin 47 | Module := Services.Modules[I]; 48 | if not SaveModule(Module) then 49 | exit; 50 | end; 51 | result := true; 52 | end; 53 | 54 | function FindSourceEditor(Module: IOTAModule; const FileExtensions: array of string): IOTASourceEditor; 55 | var 56 | i: Integer; 57 | editor: IOTAEditor; 58 | begin 59 | for i := 0 to Module.ModuleFileCount - 1 do 60 | begin 61 | editor := Module.ModuleFileEditors[i]; 62 | if not Supports(editor, IOTASourceEditor, Result) then 63 | continue; 64 | var ext := ExtractFileExt(Result.FileName).toUpper; 65 | for var scan in FileExtensions do 66 | if scan = ext then 67 | exit; 68 | end; 69 | 70 | Result := nil; 71 | end; 72 | 73 | type TCurrentSourceFileInfos = record 74 | FileName: string; 75 | Line: Integer; 76 | Column: Integer; 77 | end; 78 | 79 | function GetCurrentSourceFileInfos: TCurrentSourceFileInfos; 80 | var EditView: IOTAEditView; 81 | begin 82 | var Services := BorlandIDEServices as IOTAModuleServices; 83 | 84 | var Module := Services.CurrentModule; 85 | if Module = nil then 86 | raise Exception.Create('Current module not found'); 87 | 88 | var editor := FindSourceEditor(Module, ['.PAS', '.DPR', '.INC', '.DPK','.DFM','.FMX']); 89 | 90 | if editor = nil then 91 | raise Exception.Create('Current module does not contain a source editor'); 92 | if editor.EditViewCount = 0 then 93 | raise Exception.Create('Current module does not have visibile source code editors'); 94 | 95 | Result.FileName := editor.FileName; 96 | result.Line := -1; 97 | result.Column := -1; 98 | 99 | EditView := editor.GetEditView(0); // Assume we always work with the first view 100 | if EditView <> nil then begin 101 | Result.Line := EditView.CursorPos.Line; 102 | Result.Column := EditView.CursorPos.Col; 103 | end 104 | end; 105 | 106 | 107 | procedure OpenCurrentFileInVisualStudioCode; 108 | begin 109 | var Services := BorlandIDEServices as IOTAModuleServices; 110 | var sourceInfos: TCurrentSourceFileInfos; 111 | try 112 | sourceInfos := GetCurrentSourceFileInfos; 113 | except 114 | on E: Exception do begin 115 | ShowMessage(E.Message); 116 | Exit; 117 | end; 118 | end; 119 | 120 | var FileName := sourceInfos.FileName; 121 | var project := (BorlandIDEServices as IOTAModuleServices).GetActiveProject; 122 | 123 | if project = nil then begin 124 | ShowMessage('No active project found'); 125 | exit; 126 | end; 127 | 128 | var ProjectPath := ExtractFilePath(project.FileName); 129 | 130 | if not SaveAllModules then 131 | exit; 132 | 133 | var cmdline:string; 134 | if sourceInfos.Line < 0 then 135 | cmdline := Format('cmd /c "code --reuse-window %s"', [ProjectPath]) 136 | else 137 | cmdline := Format('cmd /c "code --reuse-window %s -g %s:%d:%d"', [ProjectPath, FileName, sourceInfos.Line, sourceInfos.Column]); 138 | 139 | 140 | var executor := TOSCommandLineExecutor.Create(nil); 141 | try 142 | executor.WorkDir := ExtractFilePath(filename); 143 | executor.CmdLine := Cmdline; 144 | executor.Execute; 145 | finally 146 | executor.Free; 147 | end; 148 | end; 149 | 150 | 151 | type 152 | TMenuHandler = class 153 | strict private 154 | Item:TMenuItem; 155 | Action: TProc; 156 | procedure OnExecute(Sender: TObject); 157 | Constructor Create(aCaption:String; aAction:TProc; aShortcut: String); 158 | class var 159 | MenuHandlers : TObjectList; 160 | FActionList: TActionList; 161 | public 162 | destructor Destroy; override; 163 | class constructor create; 164 | class destructor Destroy; 165 | class procedure AddMenuItem(NTAServices: INTAServices; aCaption:String; aAction:TProc; aShortcut: String = ''); 166 | end; 167 | 168 | class constructor TMenuHandler.Create; 169 | begin 170 | MenuHandlers := TObjectList.Create; 171 | FActionList := TActionList.Create(nil); 172 | end; 173 | 174 | class destructor TMenuHandler.Destroy; 175 | begin 176 | MenuHandlers.free; 177 | FActionList.Free; 178 | end; 179 | 180 | Constructor TMenuHandler.Create(aCaption:String; aAction:TProc;aShortcut: String); 181 | var 182 | MyAction: TAction; 183 | begin 184 | inherited Create; 185 | Action := aAction; 186 | MyAction := TAction.Create(FActionList); 187 | MyAction.Caption := aCaption; 188 | MyAction.OnExecute := OnExecute; 189 | 190 | if aShortcut <> '' then 191 | MyAction.ShortCut := TextToShortCut(aShortcut); 192 | 193 | Item := TMenuItem.Create(nil); 194 | Item.Action := MyAction; 195 | end; 196 | 197 | destructor TMenuHandler.Destroy; 198 | begin 199 | FreeAndNil(Item); 200 | inherited; 201 | end; 202 | 203 | procedure TMenuHandler.OnExecute(Sender: TObject); 204 | begin 205 | if assigned(action) then 206 | Action; 207 | end; 208 | 209 | 210 | class procedure TMenuHandler.AddMenuItem(NTAServices: INTAServices; aCaption:String; aAction:TProc; aShortcut: String = ''); 211 | begin 212 | var handler := TMenuHandler.Create(aCaption, aAction,aShortcut); 213 | MenuHandlers.Add(handler); 214 | // I am adding menu items to the top of the Tools menu because all 215 | // the menu items under "Configure Tools..." get deleted whenever you 216 | // open its dialog. 217 | NTAServices.AddActionMenu( 'ToolsMenu', nil, handler.Item ,False, True); 218 | end; 219 | 220 | procedure Register; 221 | var 222 | NTAServices: INTAServices; 223 | begin 224 | if not Supports(BorlandIDEServices, INTAServices, NTAServices) then 225 | exit; 226 | 227 | TThread.CreateAnonymousThread( 228 | procedure 229 | begin 230 | while not Application.Terminated do begin 231 | if NTAServices.MainMenu.Items.Count = 0 then begin 232 | Sleep(1000); 233 | continue; 234 | end; 235 | TThread.Synchronize(nil, 236 | procedure 237 | begin 238 | TMenuHandler.AddMenuItem(NTAServices, '-', nil); 239 | TMenuHandler.AddMenuItem(NTAServices, 'Open in Visual Studio Code', OpenCurrentFileInVisualStudioCode, 'Ctrl+\'); 240 | end); 241 | break; 242 | end; 243 | end).Start; 244 | end; 245 | 246 | end. 247 | 248 | -------------------------------------------------------------------------------- /EditInVSCode.dpk: -------------------------------------------------------------------------------- 1 | package EditInVSCode; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS ON} 17 | {$RANGECHECKS ON} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$IMPLICITBUILD ON} 29 | 30 | requires 31 | rtl, 32 | designide; 33 | 34 | contains 35 | DGVisualStudioCodeIntegration in 'DGVisualStudioCodeIntegration.pas', 36 | OSCmdLineExecutor in 'OSCmdLineExecutor.pas'; 37 | 38 | end. 39 | -------------------------------------------------------------------------------- /EditInVSCode.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {1C3F02BA-2332-4D67-A42F-26A9BE701E72} 4 | EditInVSCode.dpk 5 | 20.1 6 | VCL 7 | True 8 | Debug 9 | Win32 10 | 1 11 | Package 12 | EditInVSCode 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 | Base 30 | true 31 | 32 | 33 | true 34 | Base 35 | true 36 | 37 | 38 | true 39 | Base 40 | true 41 | 42 | 43 | true 44 | Base 45 | true 46 | 47 | 48 | true 49 | Base 50 | true 51 | 52 | 53 | true 54 | Cfg_1 55 | true 56 | true 57 | 58 | 59 | true 60 | Base 61 | true 62 | 63 | 64 | .\$(Platform)\$(Config) 65 | .\$(Platform)\$(Config) 66 | false 67 | false 68 | false 69 | false 70 | false 71 | true 72 | true 73 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) 74 | All 75 | EditInVSCode 76 | 77 | 78 | None 79 | activity-1.7.2.dex.jar;annotation-experimental-1.3.0.dex.jar;annotation-jvm-1.6.0.dex.jar;annotations-13.0.dex.jar;appcompat-1.2.0.dex.jar;appcompat-resources-1.2.0.dex.jar;billing-6.0.1.dex.jar;biometric-1.1.0.dex.jar;browser-1.4.0.dex.jar;cloud-messaging.dex.jar;collection-1.1.0.dex.jar;concurrent-futures-1.1.0.dex.jar;core-1.10.1.dex.jar;core-common-2.2.0.dex.jar;core-ktx-1.10.1.dex.jar;core-runtime-2.2.0.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;error_prone_annotations-2.9.0.dex.jar;exifinterface-1.3.6.dex.jar;firebase-annotations-16.2.0.dex.jar;firebase-common-20.3.1.dex.jar;firebase-components-17.1.0.dex.jar;firebase-datatransport-18.1.7.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-encoders-proto-16.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.1.3.dex.jar;firebase-installations-interop-17.1.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-23.1.2.dex.jar;fmx.dex.jar;fragment-1.2.5.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;kotlin-stdlib-1.8.22.dex.jar;kotlin-stdlib-common-1.8.22.dex.jar;kotlin-stdlib-jdk7-1.8.22.dex.jar;kotlin-stdlib-jdk8-1.8.22.dex.jar;kotlinx-coroutines-android-1.6.4.dex.jar;kotlinx-coroutines-core-jvm-1.6.4.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.6.1.dex.jar;lifecycle-livedata-2.6.1.dex.jar;lifecycle-livedata-core-2.6.1.dex.jar;lifecycle-runtime-2.6.1.dex.jar;lifecycle-service-2.6.1.dex.jar;lifecycle-viewmodel-2.6.1.dex.jar;lifecycle-viewmodel-savedstate-2.6.1.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;okio-jvm-3.4.0.dex.jar;play-services-ads-22.2.0.dex.jar;play-services-ads-base-22.2.0.dex.jar;play-services-ads-identifier-18.0.0.dex.jar;play-services-ads-lite-22.2.0.dex.jar;play-services-appset-16.0.1.dex.jar;play-services-base-18.1.0.dex.jar;play-services-basement-18.1.0.dex.jar;play-services-cloud-messaging-17.0.1.dex.jar;play-services-location-21.0.1.dex.jar;play-services-maps-18.1.0.dex.jar;play-services-measurement-base-20.1.2.dex.jar;play-services-measurement-sdk-api-20.1.2.dex.jar;play-services-stats-17.0.2.dex.jar;play-services-tasks-18.0.2.dex.jar;print-1.0.0.dex.jar;profileinstaller-1.3.0.dex.jar;room-common-2.2.5.dex.jar;room-runtime-2.2.5.dex.jar;savedstate-1.2.1.dex.jar;sqlite-2.1.0.dex.jar;sqlite-framework-2.1.0.dex.jar;startup-runtime-1.1.1.dex.jar;tracing-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.1.8.dex.jar;transport-runtime-3.1.8.dex.jar;user-messaging-platform-2.0.0.dex.jar;vectordrawable-1.1.0.dex.jar;vectordrawable-animated-1.1.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.7.0.dex.jar 80 | rtl;$(DCC_UsePackage) 81 | 82 | 83 | None 84 | activity-1.7.2.dex.jar;annotation-experimental-1.3.0.dex.jar;annotation-jvm-1.6.0.dex.jar;annotations-13.0.dex.jar;appcompat-1.2.0.dex.jar;appcompat-resources-1.2.0.dex.jar;billing-6.0.1.dex.jar;biometric-1.1.0.dex.jar;browser-1.4.0.dex.jar;cloud-messaging.dex.jar;collection-1.1.0.dex.jar;concurrent-futures-1.1.0.dex.jar;core-1.10.1.dex.jar;core-common-2.2.0.dex.jar;core-ktx-1.10.1.dex.jar;core-runtime-2.2.0.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;error_prone_annotations-2.9.0.dex.jar;exifinterface-1.3.6.dex.jar;firebase-annotations-16.2.0.dex.jar;firebase-common-20.3.1.dex.jar;firebase-components-17.1.0.dex.jar;firebase-datatransport-18.1.7.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-encoders-proto-16.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.1.3.dex.jar;firebase-installations-interop-17.1.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-23.1.2.dex.jar;fmx.dex.jar;fragment-1.2.5.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;kotlin-stdlib-1.8.22.dex.jar;kotlin-stdlib-common-1.8.22.dex.jar;kotlin-stdlib-jdk7-1.8.22.dex.jar;kotlin-stdlib-jdk8-1.8.22.dex.jar;kotlinx-coroutines-android-1.6.4.dex.jar;kotlinx-coroutines-core-jvm-1.6.4.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.6.1.dex.jar;lifecycle-livedata-2.6.1.dex.jar;lifecycle-livedata-core-2.6.1.dex.jar;lifecycle-runtime-2.6.1.dex.jar;lifecycle-service-2.6.1.dex.jar;lifecycle-viewmodel-2.6.1.dex.jar;lifecycle-viewmodel-savedstate-2.6.1.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;okio-jvm-3.4.0.dex.jar;play-services-ads-22.2.0.dex.jar;play-services-ads-base-22.2.0.dex.jar;play-services-ads-identifier-18.0.0.dex.jar;play-services-ads-lite-22.2.0.dex.jar;play-services-appset-16.0.1.dex.jar;play-services-base-18.1.0.dex.jar;play-services-basement-18.1.0.dex.jar;play-services-cloud-messaging-17.0.1.dex.jar;play-services-location-21.0.1.dex.jar;play-services-maps-18.1.0.dex.jar;play-services-measurement-base-20.1.2.dex.jar;play-services-measurement-sdk-api-20.1.2.dex.jar;play-services-stats-17.0.2.dex.jar;play-services-tasks-18.0.2.dex.jar;print-1.0.0.dex.jar;profileinstaller-1.3.0.dex.jar;room-common-2.2.5.dex.jar;room-runtime-2.2.5.dex.jar;savedstate-1.2.1.dex.jar;sqlite-2.1.0.dex.jar;sqlite-framework-2.1.0.dex.jar;startup-runtime-1.1.1.dex.jar;tracing-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.1.8.dex.jar;transport-runtime-3.1.8.dex.jar;user-messaging-platform-2.0.0.dex.jar;vectordrawable-1.1.0.dex.jar;vectordrawable-animated-1.1.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.7.0.dex.jar 85 | rtl;$(DCC_UsePackage) 86 | 87 | 88 | rtl;$(DCC_UsePackage) 89 | 90 | 91 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) 92 | Debug 93 | true 94 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 95 | 1033 96 | rtl;$(DCC_UsePackage) 97 | 98 | 99 | rtl;$(DCC_UsePackage) 100 | 101 | 102 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) 103 | Debug 104 | true 105 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 106 | 1033 107 | 108 | 109 | DEBUG;$(DCC_Define) 110 | true 111 | false 112 | true 113 | true 114 | true 115 | true 116 | true 117 | 118 | 119 | false 120 | 3 121 | 122 | 123 | false 124 | RELEASE;$(DCC_Define) 125 | 0 126 | 0 127 | 128 | 129 | 130 | MainSource 131 | 132 | 133 | 134 | 135 | 136 | 137 | Base 138 | 139 | 140 | Cfg_1 141 | Base 142 | 143 | 144 | Cfg_2 145 | Base 146 | 147 | 148 | 149 | Delphi.Personality.12 150 | Package 151 | 152 | 153 | 154 | EditInVSCode.dpk 155 | 156 | 157 | 158 | 159 | 160 | true 161 | 162 | 163 | 164 | 165 | true 166 | 167 | 168 | 169 | 170 | true 171 | 172 | 173 | 174 | 175 | EditInVSCode.bpl 176 | true 177 | 178 | 179 | 180 | 181 | 1 182 | 183 | 184 | 0 185 | 186 | 187 | 188 | 189 | classes 190 | 64 191 | 192 | 193 | classes 194 | 64 195 | 196 | 197 | 198 | 199 | res\xml 200 | 1 201 | 202 | 203 | res\xml 204 | 1 205 | 206 | 207 | 208 | 209 | library\lib\armeabi 210 | 1 211 | 212 | 213 | library\lib\armeabi 214 | 1 215 | 216 | 217 | 218 | 219 | library\lib\armeabi-v7a 220 | 1 221 | 222 | 223 | 224 | 225 | library\lib\mips 226 | 1 227 | 228 | 229 | library\lib\mips 230 | 1 231 | 232 | 233 | 234 | 235 | library\lib\armeabi-v7a 236 | 1 237 | 238 | 239 | library\lib\arm64-v8a 240 | 1 241 | 242 | 243 | 244 | 245 | library\lib\armeabi-v7a 246 | 1 247 | 248 | 249 | 250 | 251 | res\drawable 252 | 1 253 | 254 | 255 | res\drawable 256 | 1 257 | 258 | 259 | 260 | 261 | res\drawable-anydpi-v21 262 | 1 263 | 264 | 265 | res\drawable-anydpi-v21 266 | 1 267 | 268 | 269 | 270 | 271 | res\values 272 | 1 273 | 274 | 275 | res\values 276 | 1 277 | 278 | 279 | 280 | 281 | res\values-v21 282 | 1 283 | 284 | 285 | res\values-v21 286 | 1 287 | 288 | 289 | 290 | 291 | res\values-v31 292 | 1 293 | 294 | 295 | res\values-v31 296 | 1 297 | 298 | 299 | 300 | 301 | res\drawable-anydpi-v26 302 | 1 303 | 304 | 305 | res\drawable-anydpi-v26 306 | 1 307 | 308 | 309 | 310 | 311 | res\drawable 312 | 1 313 | 314 | 315 | res\drawable 316 | 1 317 | 318 | 319 | 320 | 321 | res\drawable 322 | 1 323 | 324 | 325 | res\drawable 326 | 1 327 | 328 | 329 | 330 | 331 | res\drawable 332 | 1 333 | 334 | 335 | res\drawable 336 | 1 337 | 338 | 339 | 340 | 341 | res\drawable-anydpi-v33 342 | 1 343 | 344 | 345 | res\drawable-anydpi-v33 346 | 1 347 | 348 | 349 | 350 | 351 | res\values 352 | 1 353 | 354 | 355 | res\values 356 | 1 357 | 358 | 359 | 360 | 361 | res\values-night-v21 362 | 1 363 | 364 | 365 | res\values-night-v21 366 | 1 367 | 368 | 369 | 370 | 371 | res\drawable 372 | 1 373 | 374 | 375 | res\drawable 376 | 1 377 | 378 | 379 | 380 | 381 | res\drawable-xxhdpi 382 | 1 383 | 384 | 385 | res\drawable-xxhdpi 386 | 1 387 | 388 | 389 | 390 | 391 | res\drawable-xxxhdpi 392 | 1 393 | 394 | 395 | res\drawable-xxxhdpi 396 | 1 397 | 398 | 399 | 400 | 401 | res\drawable-ldpi 402 | 1 403 | 404 | 405 | res\drawable-ldpi 406 | 1 407 | 408 | 409 | 410 | 411 | res\drawable-mdpi 412 | 1 413 | 414 | 415 | res\drawable-mdpi 416 | 1 417 | 418 | 419 | 420 | 421 | res\drawable-hdpi 422 | 1 423 | 424 | 425 | res\drawable-hdpi 426 | 1 427 | 428 | 429 | 430 | 431 | res\drawable-xhdpi 432 | 1 433 | 434 | 435 | res\drawable-xhdpi 436 | 1 437 | 438 | 439 | 440 | 441 | res\drawable-mdpi 442 | 1 443 | 444 | 445 | res\drawable-mdpi 446 | 1 447 | 448 | 449 | 450 | 451 | res\drawable-hdpi 452 | 1 453 | 454 | 455 | res\drawable-hdpi 456 | 1 457 | 458 | 459 | 460 | 461 | res\drawable-xhdpi 462 | 1 463 | 464 | 465 | res\drawable-xhdpi 466 | 1 467 | 468 | 469 | 470 | 471 | res\drawable-xxhdpi 472 | 1 473 | 474 | 475 | res\drawable-xxhdpi 476 | 1 477 | 478 | 479 | 480 | 481 | res\drawable-xxxhdpi 482 | 1 483 | 484 | 485 | res\drawable-xxxhdpi 486 | 1 487 | 488 | 489 | 490 | 491 | res\drawable-small 492 | 1 493 | 494 | 495 | res\drawable-small 496 | 1 497 | 498 | 499 | 500 | 501 | res\drawable-normal 502 | 1 503 | 504 | 505 | res\drawable-normal 506 | 1 507 | 508 | 509 | 510 | 511 | res\drawable-large 512 | 1 513 | 514 | 515 | res\drawable-large 516 | 1 517 | 518 | 519 | 520 | 521 | res\drawable-xlarge 522 | 1 523 | 524 | 525 | res\drawable-xlarge 526 | 1 527 | 528 | 529 | 530 | 531 | res\values 532 | 1 533 | 534 | 535 | res\values 536 | 1 537 | 538 | 539 | 540 | 541 | res\drawable-anydpi-v24 542 | 1 543 | 544 | 545 | res\drawable-anydpi-v24 546 | 1 547 | 548 | 549 | 550 | 551 | res\drawable 552 | 1 553 | 554 | 555 | res\drawable 556 | 1 557 | 558 | 559 | 560 | 561 | res\drawable-night-anydpi-v21 562 | 1 563 | 564 | 565 | res\drawable-night-anydpi-v21 566 | 1 567 | 568 | 569 | 570 | 571 | res\drawable-anydpi-v31 572 | 1 573 | 574 | 575 | res\drawable-anydpi-v31 576 | 1 577 | 578 | 579 | 580 | 581 | res\drawable-night-anydpi-v31 582 | 1 583 | 584 | 585 | res\drawable-night-anydpi-v31 586 | 1 587 | 588 | 589 | 590 | 591 | 1 592 | 593 | 594 | 1 595 | 596 | 597 | 0 598 | 599 | 600 | 601 | 602 | 1 603 | .framework 604 | 605 | 606 | 1 607 | .framework 608 | 609 | 610 | 1 611 | .framework 612 | 613 | 614 | 0 615 | 616 | 617 | 618 | 619 | 1 620 | .dylib 621 | 622 | 623 | 1 624 | .dylib 625 | 626 | 627 | 1 628 | .dylib 629 | 630 | 631 | 0 632 | .dll;.bpl 633 | 634 | 635 | 636 | 637 | 1 638 | .dylib 639 | 640 | 641 | 1 642 | .dylib 643 | 644 | 645 | 1 646 | .dylib 647 | 648 | 649 | 1 650 | .dylib 651 | 652 | 653 | 1 654 | .dylib 655 | 656 | 657 | 1 658 | .dylib 659 | 660 | 661 | 0 662 | .bpl 663 | 664 | 665 | 666 | 667 | 0 668 | 669 | 670 | 0 671 | 672 | 673 | 0 674 | 675 | 676 | 0 677 | 678 | 679 | 0 680 | 681 | 682 | 0 683 | 684 | 685 | 0 686 | 687 | 688 | 0 689 | 690 | 691 | 0 692 | 693 | 694 | 695 | 696 | 1 697 | 698 | 699 | 1 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | Contents\Resources 708 | 1 709 | 710 | 711 | Contents\Resources 712 | 1 713 | 714 | 715 | Contents\Resources 716 | 1 717 | 718 | 719 | 720 | 721 | library\lib\armeabi-v7a 722 | 1 723 | 724 | 725 | library\lib\arm64-v8a 726 | 1 727 | 728 | 729 | 1 730 | 731 | 732 | 1 733 | 734 | 735 | 1 736 | 737 | 738 | 1 739 | 740 | 741 | 1 742 | 743 | 744 | 1 745 | 746 | 747 | 1 748 | 749 | 750 | 0 751 | 752 | 753 | 754 | 755 | library\lib\armeabi-v7a 756 | 1 757 | 758 | 759 | 760 | 761 | 1 762 | 763 | 764 | 1 765 | 766 | 767 | 1 768 | 769 | 770 | 771 | 772 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 773 | 1 774 | 775 | 776 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 777 | 1 778 | 779 | 780 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 781 | 1 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 1 790 | 791 | 792 | 1 793 | 794 | 795 | 1 796 | 797 | 798 | 799 | 800 | Assets 801 | 1 802 | 803 | 804 | Assets 805 | 1 806 | 807 | 808 | 809 | 810 | Assets 811 | 1 812 | 813 | 814 | Assets 815 | 1 816 | 817 | 818 | 819 | 820 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 821 | 1 822 | 823 | 824 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 825 | 1 826 | 827 | 828 | 829 | 830 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 831 | 1 832 | 833 | 834 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 835 | 1 836 | 837 | 838 | 839 | 840 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 841 | 1 842 | 843 | 844 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 845 | 1 846 | 847 | 848 | 849 | 850 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 851 | 1 852 | 853 | 854 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 855 | 1 856 | 857 | 858 | 859 | 860 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 861 | 1 862 | 863 | 864 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 865 | 1 866 | 867 | 868 | 869 | 870 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 871 | 1 872 | 873 | 874 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 875 | 1 876 | 877 | 878 | 879 | 880 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 881 | 1 882 | 883 | 884 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 885 | 1 886 | 887 | 888 | 889 | 890 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 891 | 1 892 | 893 | 894 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 895 | 1 896 | 897 | 898 | 899 | 900 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 901 | 1 902 | 903 | 904 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 905 | 1 906 | 907 | 908 | 909 | 910 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 911 | 1 912 | 913 | 914 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 915 | 1 916 | 917 | 918 | 919 | 920 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 921 | 1 922 | 923 | 924 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 925 | 1 926 | 927 | 928 | 929 | 930 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 931 | 1 932 | 933 | 934 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 935 | 1 936 | 937 | 938 | 939 | 940 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 941 | 1 942 | 943 | 944 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 945 | 1 946 | 947 | 948 | 949 | 950 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 951 | 1 952 | 953 | 954 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 955 | 1 956 | 957 | 958 | 959 | 960 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 961 | 1 962 | 963 | 964 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 965 | 1 966 | 967 | 968 | 969 | 970 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 971 | 1 972 | 973 | 974 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 975 | 1 976 | 977 | 978 | 979 | 980 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 981 | 1 982 | 983 | 984 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 985 | 1 986 | 987 | 988 | 989 | 990 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 991 | 1 992 | 993 | 994 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 995 | 1 996 | 997 | 998 | 999 | 1000 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1001 | 1 1002 | 1003 | 1004 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1005 | 1 1006 | 1007 | 1008 | 1009 | 1010 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1011 | 1 1012 | 1013 | 1014 | ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1015 | 1 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | False 1033 | False 1034 | False 1035 | True 1036 | False 1037 | False 1038 | 1039 | 1040 | 12 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | -------------------------------------------------------------------------------- /EditInVSCode.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csm101/EditInVsCodeDelphiPlugin/e72213b3ebecbaae6213dd11b0219187d899131c/EditInVSCode.res -------------------------------------------------------------------------------- /EditInVsCodeGrp.groupproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {E47F4266-956F-4802-999A-C7C71F319019} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Default.Personality.12 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Carlo Sirna 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 | -------------------------------------------------------------------------------- /OSCmdLineExecutor.pas: -------------------------------------------------------------------------------- 1 | unit OSCmdLineExecutor; 2 | interface 3 | uses windows,classes; 4 | 5 | type 6 | TOSCommandLineExecutor = class; 7 | TOSCmdLineOutputProc = procedure(sender:TOSCommandLineExecutor; const txt:string) of object; 8 | 9 | 10 | /// 11 | /// Runs a command line command and captures its stdout/stderr 12 | /// 13 | TOSCommandLineExecutor = class(TComponent) 14 | public 15 | type TLogProc = reference to procedure(txt:string); 16 | private 17 | type 18 | TPipeHandles = record 19 | hRead, hWrite: THandle; 20 | end; 21 | var FCmdLine: string; 22 | FWorkDir: string; 23 | PipeStdOut,PipeStdErr,pipeStdIn: TPipeHandles; 24 | StartupInfo: TStartupInfo; 25 | FOnStdOut: TOSCmdLineOutputProc; 26 | FOnStdErr: TOSCmdLineOutputProc; 27 | FOnIdle : TNotifyEvent; 28 | FAnonProcStdOut,FAnonProcStdErr:TLogProc; 29 | procedure ClearPipe(var pipe:TPipeHandles); 30 | procedure InitPipe(var Pipe:TPipeHandles;SecAttr : TSecurityAttributes); 31 | procedure ClosePipe(var Pipe: TPipeHandles); 32 | function ReadPipe(var Pipe: TPipeHandles):AnsiString; 33 | procedure InitStartupInfo; 34 | procedure LogOutput(var msg:AnsiString;IsError:boolean); 35 | public 36 | function Execute: DWORD; overload; 37 | function Execute(StdErrProc,StdOutProc:TLogProc):DWORD; overload; 38 | function Execute(OutProc:TLogProc):DWORD; overload; 39 | published 40 | property OnStdOut : TOSCmdLineOutputProc read FOnStdOut write FOnStdOut; 41 | property OnStdErr : TOSCmdLineOutputProc read FOnStdErr write FOnStdErr; 42 | property OnIdle : TNotifyEvent read FOnIdle write FOnIdle; 43 | property WorkDir:string read FWorkDir write FWorkDir; 44 | property CmdLine:string read FCmdLine write FCmdLine; 45 | end; 46 | 47 | 48 | procedure Register; 49 | 50 | implementation 51 | uses sysutils; 52 | 53 | const BufSize = $4000; 54 | 55 | procedure TOSCommandLineExecutor.ClearPipe(var pipe:TPipeHandles); 56 | begin 57 | pipe.hRead := 0; 58 | pipe.hWrite := 0; 59 | end; 60 | 61 | procedure TOSCommandLineExecutor.InitPipe(var Pipe:TPipeHandles;SecAttr : TSecurityAttributes); 62 | begin 63 | if not CreatePipe (pipe.hRead, pipe.hWrite, @SecAttr, BufSize) then 64 | Raise Exception.Create('Can''t Create pipe!'); 65 | end; 66 | 67 | procedure TOSCommandLineExecutor.ClosePipe(var Pipe: TPipeHandles); 68 | begin 69 | if Pipe.hRead <> 0 then CloseHandle (Pipe.hRead); 70 | if Pipe.hWrite <> 0 then CloseHandle (Pipe.hWrite); 71 | ClearPipe(Pipe); 72 | end; 73 | 74 | function TOSCommandLineExecutor.ReadPipe(var Pipe: TPipeHandles):AnsiString; 75 | var ReadBuf: array[0..BufSize] of AnsiChar; 76 | BytesRead: Dword; 77 | begin 78 | result := ''; 79 | if not PeekNamedPipe(Pipe.hRead, nil, 0, nil, @BytesRead, nil) or (BytesRead <= 0) then exit; 80 | 81 | ReadFile( Pipe.hRead, ReadBuf, BufSize, BytesRead, nil); 82 | if BytesRead <= 0 then exit; 83 | 84 | ReadBuf[BytesRead] := #0; 85 | result := pAnsichar(@readbuf[0]); 86 | end; 87 | 88 | 89 | 90 | procedure TOSCommandLineExecutor.InitStartupInfo; 91 | begin 92 | FillChar(StartupInfo,SizeOf(TStartupInfo), 0); 93 | StartupInfo.cb := SizeOf(TStartupInfo); 94 | StartupInfo.dwFlags := STARTF_USESTDHANDLES; 95 | StartupInfo.hStdOutput := PipeStdOut.hWrite; 96 | StartupInfo.hStdError := PipeStdErr.hWrite; 97 | StartupInfo.hStdInput := PipeStdIn.hRead; 98 | StartupInfo.wShowWindow := SW_HIDE; 99 | end; 100 | 101 | 102 | procedure TOSCommandLineExecutor.LogOutput(var msg:AnsiString;IsError:boolean); 103 | var newLinePosition:integer; 104 | newLineSeparatorLength:integer; 105 | 106 | procedure LocateNewLineSeparator; 107 | var p0,p1,p2:integer; 108 | begin 109 | p0 := pos(AnsiString(#13#10),msg); 110 | p1 := pos(AnsiChar(#13),msg); 111 | p2 := pos(AnsiChar(#10),msg); 112 | 113 | if (p1 >= p0) and (p2 >= p0) then begin 114 | newLineSeparatorLength := 2; 115 | newLinePosition := p0; 116 | end else begin 117 | newLineSeparatorLength := 1; 118 | if p1 <=0 then newLinePosition := p2 119 | else if p2 <=0 then newLinePosition := p1 120 | else if p1 < p2 then newLinePosition := p1 121 | else newLinePosition := p2; 122 | end; 123 | end; 124 | 125 | var s:String; 126 | begin 127 | LocateNewLineSeparator; 128 | while newLinePosition>=1 do begin 129 | s := String(copy( msg,1,newLinePosition-1)); 130 | 131 | if IsError then begin 132 | if Assigned(FOnStdErr) then 133 | FOnStdErr(self,s); 134 | if Assigned(FAnonProcStdErr) then 135 | FAnonProcStdErr(s); 136 | end; 137 | 138 | if not IsError then begin 139 | if Assigned(FOnStdOut) then 140 | FOnStdOut(self,s); 141 | if Assigned(FAnonProcStdOut) then 142 | FAnonProcStdOut(s); 143 | end; 144 | 145 | msg := copy(msg,newLinePosition+newLineSeparatorLength,length(msg)); 146 | LocateNewLineSeparator; 147 | end; 148 | end; 149 | 150 | 151 | 152 | function TOSCommandLineExecutor.Execute(StdErrProc,StdOutProc:TLogProc):DWORD; // returns the exit code of the process 153 | var SecAttr : TSecurityAttributes; 154 | 155 | function DoExecuteProcess:DWORD; 156 | var strError,strOut:AnsiString; 157 | ProcessInformation:TProcessInformation; 158 | begin 159 | if not CreateProcess( 160 | nil, PChar(FCmdLine), nil, nil, True, 161 | NORMAL_PRIORITY_CLASS or CREATE_NO_WINDOW, 162 | nil, PChar(FWorkDir), 163 | StartupInfo, 164 | ProcessInformation ) 165 | then Raise Exception.create('Cannot create process for '+FCmdLine+ #13#10+SysErrorMessage(GetLastError)); 166 | result := STILL_ACTIVE; 167 | 168 | strError := ''; 169 | strOut := ''; 170 | try 171 | repeat 172 | WaitForSingleObject(ProcessInformation.hProcess, 100); 173 | GetExitCodeProcess(ProcessInformation.hProcess,result); 174 | if assigned(FOnIdle) then 175 | FOnIdle(self); 176 | strError := strError + ReadPipe(PipeStdErr); 177 | StrOut := StrOut + ReadPipe(PipeStdOut); 178 | LogOutput(strError,true); 179 | LogOutput(strOut,False); 180 | until result <> STILL_ACTIVE; 181 | 182 | if not GetExitCodeProcess(ProcessInformation.hProcess,result) then 183 | Raise Exception.Create('Cannot get exit code!'); 184 | 185 | result := result; 186 | finally 187 | if result = STILL_ACTIVE then TerminateProcess(ProcessInformation.hProcess, 1); 188 | CloseHandle(ProcessInformation.hProcess); 189 | CloseHandle(ProcessInformation.hThread); 190 | ProcessInformation.hProcess := 0; 191 | end; 192 | end; 193 | 194 | begin 195 | FAnonProcStdOut := StdOutProc; 196 | FAnonProcStdErr := StdErrProc; 197 | try 198 | SecAttr.nLength := SizeOf(SecAttr); 199 | SecAttr.lpSecurityDescriptor := nil; 200 | SecAttr.bInheritHandle := TRUE; 201 | 202 | ClearPipe(pipeStdIn); 203 | ClearPipe(PipeStdErr); 204 | ClearPipe(PipeStdOut); 205 | try 206 | InitPipe(PipeStdIn,SecAttr); 207 | InitPipe(PipeStdErr,SecAttr); 208 | InitPipe(PipeStdOut,SecAttr); 209 | // child process must not inherit stdin/stdout/stderr stream handles from the current process 210 | if not SetHandleInformation(pipeStdIn.hWrite, HANDLE_FLAG_INHERIT, 0) then 211 | Raise Exception.Create('error calling SetHandleInformation for pipeStdIn.hWrite'); 212 | if not SetHandleInformation(pipeStdOut.hRead, HANDLE_FLAG_INHERIT, 0) then 213 | Raise Exception.Create('error calling SetHandleInformation for pipeStdOut.hRead'); 214 | if not SetHandleInformation(PipeStdErr.hRead, HANDLE_FLAG_INHERIT, 0) then 215 | Raise Exception.Create('error calling SetHandleInformation for PipeStdErr.hRead'); 216 | InitStartupInfo; 217 | result := DoExecuteProcess; 218 | finally 219 | ClosePipe(PipeStdOut); 220 | ClosePipe(PipeStdErr); 221 | ClosePipe(PipeStdIn); 222 | end; 223 | finally 224 | FAnonProcStdOut := nil; 225 | FAnonProcStdErr := nil; 226 | end; 227 | end; 228 | 229 | function TOSCommandLineExecutor.Execute: DWORD; 230 | begin 231 | result := Execute(nil,nil); 232 | end; 233 | 234 | function TOSCommandLineExecutor.Execute(OutProc:TLogProc):DWORD; 235 | begin 236 | result := Execute(OutProc,OutProc); 237 | end; 238 | 239 | procedure Register; 240 | begin 241 | // RegisterComponents('CSTools',[TOSCommandLineExecutor]); 242 | end; 243 | 244 | end. 245 | 246 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open in Visual Studio Code (Delphi plugin) 2 | 3 | *If you want to have GitHub Copilot AI doing pair-programming with you when you program in Delphi, you need Visual Studio Code and this plugin.* 4 | 5 | This Delphi plugin will create a "Tools->Open In Visual Studio Code" menù item in your RAD Studio IDE. 6 | 7 | Selecting this option will do just these two simple things: 8 | 9 | 1. All modified files opened in your ide will be saved. 10 | 2. The Delphi source file you were editing will be opened in Visual Studio Code, at the exact line you were editing in Rad Studio. 11 | 3. Existing Visual Studio Code instances will be reused (the same source file won't be opened multiple times in different editors) 12 | 13 | What's so great about using Visual Studio Code as a code editor? 14 | 1. **Copilot with Delphi**: (this is the main reason I wrote this plugin): if you have the Copilot extension installed in Visual Studio Code, it will work perfectly with your Delphi code. This is an incredible game-changer. 15 | 2. Visual Studio Code is simply a far better code editor than the one integrated into the RAD Studio IDE (for example it supports multiple cursors). 16 | 17 | ## Some cool things you can do with Copilot 18 | Copilot is an AI specifically trained for programming and it will run inside your visual studio instance: it will understand your code (and your comments!) and it will suggest you code unbelievable code completions. 19 | It will really feel like you are coding with the help of another programmer working side by side with you. 20 | 21 | Here are some simple screenshots of Copilot suggesting code: 22 | 23 | Here it is completing the `TDaysOfTheWeek` enum (the suggestion is the part in gray: you just have to press "TAB" to accept it) 24 | 25 | ![Delphi Programming TDaysOfTheWeek being auto-completed by Copilot](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/62d3115c-4f08-4ad5-a8b5-a6e1238c8b0d) 26 | 27 | Same for the `TLanguages` enum: 28 | 29 | ![Delphi Programming TLanguages being auto-completed by Copilot](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/96be5322-b890-49fd-831f-dd2620e47e2f) 30 | 31 | Here it suggests a whole function implementation: 32 | 33 | ![Whole Delphi Programming function implementation auto-completed by Copilot](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/57f5c114-beaf-4e9d-bff1-608c1bbb1d6a) 34 | 35 | 36 | You can also chat with the Copilot about a portion of the code you have selected and ask it to explain what it does: 37 | 38 | ![Copilot explaining Delphi Programming Code](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/d1c2526c-81f5-4420-99f5-cb4d682b14c1) 39 | 40 | This is just the tip of the iceberg: you can ask the Copilot to modify your code (with prompts like "use meaningful variable names", "use italian for variable names", "refactor this function by using simpler and smaller local functions", "add comments to this code", "write xml documentation for this class"). 41 | 42 | ## How to use this plugin (Delphi side) 43 | 44 | 1. Download the source code from this repository 45 | 2. Open the package project, build it and install it. 46 | 3. You need to activate this option in Delphi (under Tools->Options->Editor->Language->Delphi->Code Insight). 47 | (This is necessary to make the LSP Visual Studio Code extension work correctly). 48 | 49 | ![How to use this plugin (Delphi side)](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/c254e016-c44a-40ba-afab-a8b5c6246089) 50 | 51 | 3. (optional) Disable the confirmation requests about reloading a file that is unchanged in the IDE and has been changed externally. 52 | This will make Delphi reload automatically a file that has been changed on the file system (this applies only if you don't have any pending changes in the editor). 53 | This will make it more "fluid" to switch back from Visual Studio Code to Delphi. 54 | 55 | ![Disable the confirmation requests](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/c0536311-dc6b-462a-a590-09ab5d715765) 56 | 57 | ## Install and configure Visual Studio Code 58 | 59 | 1. Download and install VSCode from here https://code.visualstudio.com/download 60 | 2. Launch VSCode and install the DelphiLSP extension from its marketplace 61 | 62 | ![Launch VSCode and install the DelphiLSP extension from its marketplace](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/d0a6bbf1-8a30-4aa7-bc3a-6c6d1b9a32f7) 63 | 64 | You will also need the copilot extensions: 65 | 66 | ![copilot extension](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/aa9473a4-ce94-4130-8007-845112bdf1d1) 67 | 68 | ## How to enable copilot. 69 | 1. Create a GitHub account (if you already haven't one) 70 | 2. Subscribe for Copilot (look for the menu option "Your Copilot" in the popup menu that opens when you click on your avatar in the top-right corner). 71 | The first 30 days are a free trial, then it costs 10 dollars/month... I think it is worth every penny.) 72 | 3. Back in Visual Studio Code, sign in with copilot. 73 | 74 | ![In Visual Studio Code, sign in with copilot](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/89ac2bba-b7a9-4be4-a57d-e4f082e20ced) 75 | 76 | ## Some Handy Visual Studio Code Configuration Tips 77 | ### Enable AutoSave in Visual Studio 78 | This will make Visual Studio save automatically all the changes to the source file, so whenever you switch back to Delphi you will find in Delphi all the changes you made. If you disabled the reload prompt in Delphi as I suggested, you will get a much better experience. 79 | *Just remember that Delphi does NOT do an auto-save when you switch back to Visual Studio Code, unless you switch from Delphi to VSC by using the tools->Open In Visual Studio Code menu option* 80 | 81 | This is quite easy, just check this option in the File menu: 82 | 83 | ![check this option in the File menu](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/5ee10c81-3a23-4a09-832f-74d6453b5c7d) 84 | 85 | ### Enable file encoding auto-detection 86 | By default, VSC opens all source files using the UTF8 encoding. It is very likely that you still have some Delphi source files encoded in your local ANSI 8-bit charset. 87 | 88 | Search the "Auto Guess Encoding" under VSCode settings, and enable it 89 | 90 | ![Search the "Auto Guess Encoding" under VSCode settings, and enable it](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/22c8a93a-e14d-4bf1-8031-a08f205d8526) 91 | 92 | *you can access the settings tab by pressing "CTRL+," (at least this is the key combination on my Italian keyboard), or by clicking the "gear" icon in the bottomleft corner of vsc.* 93 | 94 | ### Enable "ligatures" and install a font that supports them 95 | This is just something "fancy" but I like it. Maybe you'll like it too: If you use a ligature-supporting font like firacode you type ">=" but you see "≥": all special character sequences commonly used in programming languages get rendered as the symbol that they should actually represent. 96 | 97 | 1. Install this font in Windows: https://github.com/tonsky/FiraCode 98 | 2. look for "Font Family" inside VSCode settings tab and insert "'Fira Code'" as the first font in the comma-separated list (my current setup contains this string, for example, "'Fira Code', Consolas, 'Courier New', monospace") 99 | 3. look for "ligatures" in vscode settings. You will see that you have to select "edit in settings.json" to modify it. Press that "edit in settings.json" link and add "editor.fontLigatures": true, to your configuration file, right before the line about autoGuessEncoding. Mine looks like this: 100 | 101 | ![Enable "ligatures" and install a font that supports them](https://github.com/csm101/EditInVsCodeDelphiPlugin/assets/5736859/710b3e2c-0bc7-4aef-a334-c9a3edc30520) 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | --------------------------------------------------------------------------------