1 #include "ide.h"
2
3 namespace Upp {
4 bool IsSystemThemeDark();
5 };
6
RunArgs()7 void Ide::RunArgs() {
8 WithRunLayout<TopWindow> dlg;
9 CtrlLayoutOKCancel(dlg, "Run options");
10 dlg.Sizeable().Zoomable();
11
12 #ifndef PLATFORM_POSIX
13 dlg.consolemode.Hide();
14 dlg.console_label.Hide();
15 #endif
16
17 #ifndef PLATFORM_WIN32
18 dlg.advanced_label.Hide();
19 dlg.disable_uhd.Hide();
20 dlg.darkmode.Hide();
21 #endif
22
23 #ifdef PLATFORM_WIN32
24 dlg.darkmode.SetLabel(IsSystemThemeDark() ? "Run in light mode" : "Run in dark mode");
25 #endif
26
27 SelectDirButton dir_browse("Run in folder");
28 dir_browse.Attach(dlg.dir);
29 dlg.dir = rundir.ToWString();
30
31 dlg.arg <<= runarg;
32
33 {
34 StringStream ss(recent_runarg);
35 dlg.arg.SerializeList(ss);
36 }
37
38 SaveFileButton stdout_browse("Save STDOUT as");
39 stdout_browse.Type("Text files (*.txt)", "*.txt").AllFilesType();
40 stdout_browse.Attach(dlg.stdout_file);
41
42 {
43 StringStream ss(recent_stdout_file);
44 dlg.stdout_file.SerializeList(ss);
45 dlg.stdout_file <<= stdout_file;
46 }
47
48 dlg.runmode <<= runmode;
49 dlg.external = runexternal;
50 dlg.consolemode = consolemode;
51 dlg.utf8 <<= console_utf8;
52 dlg.runmode <<= dlg.Breaker(222);
53 dlg.disable_uhd <<= disable_uhd;
54 dlg.darkmode <<= darkmode;
55
56 auto Ins = [&](bool file) {
57 int l, h;
58 dlg.arg.GetSelection(l, h);
59 String s = file ? SelectFileOpen("All files\t*.*") : SelectDirectory();
60 dlg.arg.SetSelection(l, h);
61 if(s.GetCount()) {
62 if(s.Find(' ') >= 0)
63 s = '\"' + s + '\"';
64 dlg.arg.Insert(s);
65 }
66 };
67 dlg.ifile.SetImage(CtrlImg::File());
68 dlg.ifile << [&] { Ins(true); };
69 dlg.idir.SetImage(CtrlImg::Dir());
70 dlg.idir << [&] { Ins(false); };
71
72 for(;;) {
73 bool b = ~dlg.runmode == RUN_FILE;
74 dlg.stdout_file_lbl.Enable(b);
75 dlg.stdout_file.Enable(b);
76 int rm = ~dlg.runmode;
77 dlg.stdout_file.Enable(rm == RUN_FILE || rm == RUN_FILE_CONSOLE);
78 dlg.utf8.Enable(rm != RUN_WINDOW);
79 switch(dlg.Run()) {
80 case IDOK:
81 rundir = ~dlg.dir;
82 runarg = ~dlg.arg;
83 runmode = ~dlg.runmode;
84 runexternal = dlg.external;
85 consolemode = dlg.consolemode;
86 console_utf8 = ~dlg.utf8;
87 stdout_file = ~dlg.stdout_file;
88 disable_uhd = ~dlg.disable_uhd;
89 darkmode = ~dlg.darkmode;
90 dlg.arg.AddHistory();
91 {
92 StringStream ss;
93 dlg.arg.SerializeList(ss);
94 recent_runarg = ss;
95 }
96 {
97 StringStream ss;
98 dlg.stdout_file.SerializeList(ss);
99 recent_stdout_file = ss;
100 }
101 return;
102
103 case IDCANCEL:
104 return;
105 }
106 }
107 }
108
CreateHostRunDir()109 One<Host> Ide::CreateHostRunDir()
110 {
111 One<Host> h = CreateHost(darkmode, disable_uhd);
112 if(IsNull(rundir))
113 h->ChDir(GetFileFolder(target));
114 else
115 h->ChDir(rundir);
116 return h;
117 }
118
ShouldHaveConsole()119 bool Ide::ShouldHaveConsole()
120 {
121 return decode(consolemode, 0, FindIndex(SplitFlags(mainconfigparam, true), "GUI") < 0,
122 1, true, false);
123 }
124
BuildAndExecute()125 void Ide::BuildAndExecute()
126 {
127 if(Build()) {
128 String targetExt = GetFileExt(target);
129 if(targetExt == ".apk")
130 ExecuteApk();
131 else
132 ExecuteBinary();
133 }
134 }
135
ExecuteBinary()136 void Ide::ExecuteBinary()
137 {
138 int time = msecs();
139 One<Host> h = CreateHostRunDir();
140 h->ChDir(Nvl(rundir, GetFileFolder(target)));
141 String cmdline;
142 if(!runexternal)
143 cmdline << '\"' << h->GetHostPath(target) << "\" ";
144 cmdline << ToSystemCharset(runarg);
145
146 int exitcode;
147 switch(runmode) {
148 case RUN_WINDOW:
149 HideBottom();
150 h->Launch(cmdline, ShouldHaveConsole());
151 break;
152 case RUN_CONSOLE:
153 ShowConsole();
154 PutConsole(String().Cat() << "Executing: " << cmdline);
155 console.Sync();
156 exitcode = h->ExecuteWithInput(cmdline, console_utf8);
157 PutConsole("Finished in " + GetPrintTime(time) + ", exit code: " + AsString(exitcode));
158 break;
159 case RUN_FILE: {
160 HideBottom();
161 String fn;
162 if(IsNull(stdout_file))
163 fn = ForceExt(target, ".ol");
164 else
165 fn = stdout_file;
166 FileOut out(fn);
167 if(!out) {
168 PromptOK("Unable to open output file [* " + DeQtf(stdout_file) + "] !");
169 return;
170 }
171 if(h->Execute(cmdline, out, console_utf8) >= 0) {
172 out.Close();
173 EditFile(fn);
174 }
175 }
176 }
177 }
178
179 class SelectAndroidDeviceDlg : public WithSelectAndroidDeviceLayout<TopWindow> {
180 typedef SelectAndroidDeviceDlg CLASSNAME;
181
182 public:
183 SelectAndroidDeviceDlg(AndroidSDK* sdk);
184
GetDeviceCount() const185 int GetDeviceCount() const { return devicesArray.GetCount(); }
186 String GetSelectedSerial() const;
187
188 private:
189 void LoadDevices();
190
191 void OnRefresh();
192
193 private:
194 AndroidSDK* sdk;
195 };
196
SelectAndroidDeviceDlg(AndroidSDK * sdk)197 SelectAndroidDeviceDlg::SelectAndroidDeviceDlg(AndroidSDK* sdk) :
198 sdk(sdk)
199 {
200 CtrlLayoutOKCancel(*this, "Android device selection");
201
202 devicesArray.AddColumn("Serial Number");
203 devicesArray.AddColumn("State");
204
205 refresh <<= THISBACK(OnRefresh);
206
207 LoadDevices();
208 }
209
GetSelectedSerial() const210 String SelectAndroidDeviceDlg::GetSelectedSerial() const
211 {
212 int row = devicesArray.IsCursor() ? devicesArray.GetCursor() : 0;
213 return devicesArray.GetCount() ? devicesArray.Get(row, 0) : "";
214 }
215
LoadDevices()216 void SelectAndroidDeviceDlg::LoadDevices()
217 {
218 Vector<AndroidDevice> devices = sdk->FindDevices();
219 for(int i = 0; i < devices.GetCount(); i++) {
220 devicesArray.Add(devices[i].GetSerial(), devices[i].GetState());
221 }
222
223 if(devicesArray.GetCount()) {
224 devicesArray.GoBegin();
225 ok.Enable();
226 }
227 else
228 ok.Disable();
229 }
230
OnRefresh()231 void SelectAndroidDeviceDlg::OnRefresh()
232 {
233 devicesArray.Clear();
234 LoadDevices();
235 }
236
ExecuteApk()237 void Ide::ExecuteApk()
238 {
239 AndroidSDK sdk(GetAndroidSdkPath(), true);
240 if(!sdk.Validate())
241 return;
242
243 SelectAndroidDeviceDlg select(&sdk);
244 if(select.GetDeviceCount() != 1 && select.Run() != IDOK)
245 return;
246 if(!select.GetDeviceCount())
247 return;
248
249 One<Host> host = CreateHost(darkmode, disable_uhd);
250 Apk apk(target, sdk);
251 String packageName = apk.FindPackageName();
252 String activityName = apk.FindLaunchableActivity();
253
254 Adb adb = sdk.MakeAdb();
255 adb.SetSerial(select.GetSelectedSerial());
256 host->Execute(adb.MakeInstallCmd(target));
257
258 if(!packageName.IsEmpty() && !activityName.IsEmpty())
259 host->Execute(adb.MakeLaunchOnDeviceCmd(packageName, activityName));
260 }
261
BuildAndDebug0(const String & srcfile)262 void Ide::BuildAndDebug0(const String& srcfile)
263 {
264 if(Build()) {
265 One<Host> h = CreateHostRunDir();
266 h->ChDir(GetFileFolder(target));
267 VectorMap<String, String> bm = GetMethodVars(method);
268 String dbg = Nvl(bm.Get("DEBUGGER", Null), "gdb");
269 h->Launch('\"' + dbg + "\" \"" + h->GetHostPath(target) + "\"", true);
270 }
271 }
272
BuildAndExtDebug()273 void Ide::BuildAndExtDebug()
274 {
275 BuildAndDebug0(Null);
276 }
277
BuildAndExtDebugFile()278 void Ide::BuildAndExtDebugFile()
279 {
280 BuildAndDebug0(editfile);
281 }
282
283 One<Debugger> GdbCreate(One<Host>&& host, const String& exefile, const String& cmdline, bool console);
284
285 #ifdef PLATFORM_WIN32
286 One<Debugger> PdbCreate(One<Host>&& host, const String& exefile, const String& cmdline, bool clang);
287 #endif
288
BuildAndDebug(bool runto)289 void Ide::BuildAndDebug(bool runto)
290 {
291 VectorMap<String, String> bm = GetMethodVars(method);
292 String builder = bm.Get("BUILDER", "");
293
294 // TODO: implement debugging on android
295 if(builder == "ANDROID") {
296 BuildAndExecute();
297 return;
298 }
299
300 if(!Build())
301 return;
302 if(!FileExists(target))
303 return;
304 if(designer && !editfile_isfolder)
305 EditAsText();
306 One<Host> host = CreateHostRunDir();
307 host->ChDir(Nvl(rundir, GetFileFolder(target)));
308 HideBottom();
309 editor.Disable();
310
311 bool console = ShouldHaveConsole();
312
313 #ifdef PLATFORM_WIN32
314 if(findarg(builder, "GCC") < 0) // llvm-mingw can generate pdb symbolic info
315 debugger = PdbCreate(pick(host), target, runarg, builder == "CLANG");
316 else
317 #endif
318 debugger = GdbCreate(pick(host), target, runarg, console);
319
320 if(!debugger) {
321 IdeEndDebug();
322 SetBar();
323 editor.Enable();
324 return;
325 }
326 debuglock = 0;
327 const Workspace& wspc = IdeWorkspace();
328 for(int i = 0; i < wspc.GetCount(); i++) {
329 const Package& pk = wspc.GetPackage(i);
330 String n = wspc[i];
331 for(int i = 0; i < pk.file.GetCount(); i++) {
332 String file = SourcePath(n, pk.file[i]);
333 LineInfo& ln = Filedata(file).lineinfo;
334 for(int i = 0; i < ln.GetCount(); i++) {
335 LineInfoRecord& lr = ln[i];
336 if(!lr.breakpoint.IsEmpty())
337 if(!debugger->SetBreakpoint(file, lr.lineno, lr.breakpoint)) {
338 lr.breakpoint = "\xe";
339 if(PathIsEqual(file, editfile))
340 editor.SetBreakpoint(lr.lineno, "\xe");
341 }
342 }
343 }
344 }
345 SetBar();
346 editor.Enable();
347 if(runto) {
348 if(!debugger->RunTo())
349 IdeEndDebug();
350 }
351 else
352 debugger->Run();
353 }
354
DebugClearBreakpoints()355 void Ide::DebugClearBreakpoints()
356 {
357 const Workspace& wspc = IdeWorkspace();
358 for(int i = 0; i < wspc.GetCount(); i++) {
359 const Package& pk = wspc.GetPackage(i);
360 String n = wspc[i];
361 for(int i = 0; i < pk.file.GetCount(); i++) {
362 String file = SourcePath(n, pk.file[i]);
363 LineInfo& ln = Filedata(file).lineinfo;
364 if(debugger)
365 for(int i = 0; i < ln.GetCount(); i++) {
366 const LineInfoRecord& lr = ln[i];
367 if(!lr.breakpoint.IsEmpty())
368 debugger->SetBreakpoint(file, lr.lineno, "");
369 }
370 ClearBreakpoints(ln);
371 }
372 }
373 editor.ClearBreakpoints();
374 }
375
OnBreakpoint(int i)376 void Ide::OnBreakpoint(int i)
377 {
378 if(!editfile.IsEmpty() && !designer && debugger) {
379 String q = editor.GetBreakpoint(i);
380 if(q[0] != 0xe && !debugger->SetBreakpoint(editfile, i, q)) {
381 auto event = editor.WhenBreakpoint;
382 editor.WhenBreakpoint = {};
383
384 if(!q.IsEmpty())
385 editor.SetBreakpoint(i, Null);
386 else
387 editor.SetBreakpoint(i, "1");
388
389 editor.WhenBreakpoint = event;
390 }
391 }
392 }
393
DebugToggleBreak()394 void Ide::DebugToggleBreak()
395 {
396 if(editfile.IsEmpty() || designer)
397 return;
398 int ln = editor.GetCursorLine();
399 String brk = editor.GetBreakpoint(ln);
400 if(!brk.IsEmpty())
401 editor.SetBreakpoint(ln, Null);
402 else
403 editor.SetBreakpoint(ln, "1");
404 editor.RefreshFrame();
405 }
406
ConditionalBreak()407 void Ide::ConditionalBreak()
408 {
409 if(editfile.IsEmpty() || designer)
410 return;
411 int ln = editor.GetCursorLine();
412 String brk = editor.GetBreakpoint(ln);
413 if(brk == "\xe")
414 brk = "1";
415
416 Index<String> cfg = PackageConfig(IdeWorkspace(), 0, GetMethodVars(method), mainconfigparam,
417 *CreateHost(darkmode, disable_uhd), *CreateBuilder(~CreateHostRunDir()));
418 #ifdef PLATFORM_WIN32
419 if(cfg.Find("MSC") >= 0) {
420 if(EditPDBExpression("Conditional breakpoint", brk, NULL))
421 editor.SetBreakpoint(ln, brk);
422 }
423 else
424 #endif
425 if(EditText(brk, "Conditional breakpoint", "Condition"))
426 editor.SetBreakpoint(ln, brk);
427 editor.RefreshFrame();
428 }
429
StopDebug()430 void Ide::StopDebug()
431 {
432 if(debugger)
433 debugger->Stop();
434 console.Kill();
435 PosSync();
436 }
437
EditorTip(CodeEditor::MouseTip & mt)438 bool Ide::EditorTip(CodeEditor::MouseTip& mt)
439 {
440 if(!debugger)
441 return false;
442 DR_LOG("EditorTip");
443 int pos = mt.pos;
444 String e;
445 String sep;
446 while(pos >= 0) {
447 String b = editor.ReadIdBackPos(pos, false);
448 if(b.GetCount() == 0)
449 break;
450 e = b + sep + e;
451 sep = ".";
452 while(pos > 0 && editor.GetChar(pos - 1) == ' ')
453 pos--;
454 if(pos > 0 && editor.GetChar(pos - 1) == '.')
455 --pos;
456 else
457 if(pos >= 2 && editor.GetChar(pos - 1) == ':' && editor.GetChar(pos - 2) == ':') {
458 pos -= 2;
459 sep = "::";
460 }
461 else
462 if(pos >= 2 && editor.GetChar(pos - 1) == '>' && editor.GetChar(pos - 2) == '-')
463 pos -= 2;
464 else
465 break;
466 while(pos > 0 && editor.GetChar(pos - 1) == ' ')
467 pos--;
468 }
469 DR_LOG("debugger->Tip");
470 return debugger->Tip(e, mt);
471 }
472