Subversion Repositories fastphp

Rev

Rev 20 | Rev 22 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
8 daniel-mar 1
unit EditorMain;
2 daniel-mar 2
 
4 daniel-mar 3
(*
4
  This program requires
5
  - Microsoft Internet Controls (TWebBrowser)
6
    If you are using Delphi 10.1 Starter Edition, please import the ActiveX TLB
7
    "Microsoft Internet Controls"
8
  - SynEdit
9
    You can obtain SynEdit via Embarcadero GetIt
10
*)
11
 
2 daniel-mar 12
// TODO: localize
13
// TODO: wieso geht copy paste im twebbrowser nicht???
14
// Wieso dauert webbrowser1 erste kompilierung so lange???
5 daniel-mar 15
// TODO: wieso kommt syntax fehler zweimal? einmal stderr einmal stdout?
16
// TODO: Browser titlebar (link preview)
21 daniel-mar 17
// TODO: search prev
18
// TODO: wenn man schrift größer/kleiner macht, soll die bildschirmseite zentriert bleiben
19
// TODO: todo liste
2 daniel-mar 20
 
21
// Future ideas
21 daniel-mar 22
// - code explorer / code insight
2 daniel-mar 23
// - verschiedene php versionen?
24
// - webbrowser1 nur laden, wenn man den tab anwählt?
25
// - doppelklick auf tab soll diesen schließen
5 daniel-mar 26
// - Onlinehelp (www) aufrufen
13 daniel-mar 27
// - Let all colors be adjustable
21 daniel-mar 28
// - code in bildschirmmitte (horizontal)?
2 daniel-mar 29
 
30
interface
31
 
32
uses
33
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
4 daniel-mar 34
  Dialogs, StdCtrls, OleCtrls, ComCtrls, ExtCtrls, ToolWin, IniFiles,
13 daniel-mar 35
  SynEditHighlighter, SynHighlighterPHP, SynEdit, SHDocVw_TLB, FindReplace,
20 daniel-mar 36
  System.Actions, Vcl.ActnList, System.UITypes;
2 daniel-mar 37
 
38
type
39
  TForm1 = class(TForm)
40
    PageControl1: TPageControl;
41
    PlaintextTabSheet: TTabSheet;
42
    HtmlTabSheet: TTabSheet;
43
    Memo2: TMemo;
44
    WebBrowser1: TWebBrowser;
45
    Splitter1: TSplitter;
46
    PageControl2: TPageControl;
20 daniel-mar 47
    CodeTabsheet: TTabSheet;
2 daniel-mar 48
    HelpTabsheet: TTabSheet;
49
    WebBrowser2: TWebBrowser;
50
    OpenDialog1: TOpenDialog;
51
    Panel1: TPanel;
52
    OpenDialog3: TOpenDialog;
4 daniel-mar 53
    SynEdit1: TSynEdit;
54
    SynPHPSyn1: TSynPHPSyn;
5 daniel-mar 55
    Panel2: TPanel;
56
    SynEditFocusTimer: TTimer;
57
    Button1: TButton;
58
    Button2: TButton;
59
    Button3: TButton;
13 daniel-mar 60
    Button4: TButton;
61
    Button5: TButton;
62
    Button6: TButton;
63
    ActionList: TActionList;
64
    ActionFind: TAction;
65
    ActionReplace: TAction;
66
    ActionFindNext: TAction;
67
    ActionGoto: TAction;
68
    ActionSave: TAction;
69
    ActionHelp: TAction;
70
    ActionRun: TAction;
71
    ActionESC: TAction;
72
    Button7: TButton;
15 daniel-mar 73
    ActionOpen: TAction;
74
    Button8: TButton;
2 daniel-mar 75
    procedure Run(Sender: TObject);
76
    procedure FormShow(Sender: TObject);
77
    procedure FormCreate(Sender: TObject);
78
    procedure FormDestroy(Sender: TObject);
79
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
80
    procedure PageControl2Changing(Sender: TObject; var AllowChange: Boolean);
5 daniel-mar 81
    procedure Memo2DblClick(Sender: TObject);
82
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
83
      const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
84
      Headers: OleVariant; var Cancel: WordBool);
85
    procedure SynEditFocusTimerTimer(Sender: TObject);
13 daniel-mar 86
    procedure ActionFindExecute(Sender: TObject);
87
    procedure ActionReplaceExecute(Sender: TObject);
88
    procedure ActionFindNextExecute(Sender: TObject);
89
    procedure ActionGotoExecute(Sender: TObject);
90
    procedure ActionSaveExecute(Sender: TObject);
91
    procedure ActionHelpExecute(Sender: TObject);
92
    procedure ActionRunExecute(Sender: TObject);
93
    procedure ActionESCExecute(Sender: TObject);
94
    procedure SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
95
      MousePos: TPoint; var Handled: Boolean);
96
    procedure SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
97
      MousePos: TPoint; var Handled: Boolean);
15 daniel-mar 98
    procedure ActionOpenExecute(Sender: TObject);
99
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
17 daniel-mar 100
    procedure Memo2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
2 daniel-mar 101
  private
102
    CurSearchTerm: string;
103
    HlpPrevPageIndex: integer;
13 daniel-mar 104
    SrcRep: TFindReplace;
2 daniel-mar 105
    procedure Help;
5 daniel-mar 106
    function MarkUpLineReference(cont: string): string;
2 daniel-mar 107
  protected
108
    ChmIndex: TMemIniFile;
19 daniel-mar 109
    FScrapFile: string;
5 daniel-mar 110
    procedure GotoLineNo(LineNo:integer);
2 daniel-mar 111
    function GetScrapFile: string;
112
  end;
113
 
114
var
115
  Form1: TForm1;
116
 
117
implementation
118
 
119
{$R *.dfm}
120
 
121
uses
15 daniel-mar 122
  Functions, StrUtils, WebBrowserUtils, FastPHPUtils, Math, ShellAPI;
2 daniel-mar 123
 
13 daniel-mar 124
// TODO: FindPrev ?
125
procedure TForm1.ActionFindNextExecute(Sender: TObject);
126
begin
127
  SrcRep.FindNext;
128
end;
129
 
130
procedure TForm1.ActionGotoExecute(Sender: TObject);
5 daniel-mar 131
var
132
  val: string;
133
  lineno: integer;
134
begin
13 daniel-mar 135
  // TODO: VK_LMENU does not work! only works with AltGr but not Alt
136
  // http://stackoverflow.com/questions/16828250/delphi-xe2-how-to-prevent-the-alt-key-stealing-focus ?
5 daniel-mar 137
 
13 daniel-mar 138
  InputQuery('Go to', 'Line number:', val);
139
  if not TryStrToInt(val, lineno) then
140
  begin
141
    if SynEdit1.CanFocus then SynEdit1.SetFocus;
142
    exit;
143
  end;
144
  GotoLineNo(lineno);
145
end;
5 daniel-mar 146
 
13 daniel-mar 147
procedure TForm1.ActionHelpExecute(Sender: TObject);
148
begin
149
  Help;
150
  if PageControl2.ActivePage = HelpTabsheet then
151
    WebBrowser2.SetFocus
20 daniel-mar 152
  else if PageControl2.ActivePage = CodeTabsheet then
13 daniel-mar 153
    SynEdit1.SetFocus;
154
end;
8 daniel-mar 155
 
15 daniel-mar 156
procedure TForm1.ActionOpenExecute(Sender: TObject);
157
begin
158
  If OpenDialog3.Execute then
159
  begin
160
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar(OpenDialog3.FileName), '', SW_NORMAL);
161
  end;
162
end;
163
 
13 daniel-mar 164
procedure TForm1.ActionReplaceExecute(Sender: TObject);
165
begin
166
  SrcRep.ReplaceExecute;
167
end;
5 daniel-mar 168
 
13 daniel-mar 169
procedure TForm1.ActionRunExecute(Sender: TObject);
170
begin
171
  Run(Sender);
172
  SynEdit1.SetFocus;
173
end;
5 daniel-mar 174
 
13 daniel-mar 175
procedure TForm1.ActionSaveExecute(Sender: TObject);
176
begin
177
  SynEdit1.Lines.SaveToFile(GetScrapFile);
16 daniel-mar 178
  SynEdit1.Modified := false;
13 daniel-mar 179
end;
180
 
181
procedure TForm1.ActionESCExecute(Sender: TObject);
182
begin
183
  if (HlpPrevPageIndex <> -1) and (PageControl2.ActivePage = HelpTabSheet) and
184
     (HelpTabsheet.TabVisible) then
185
  begin
186
    PageControl2.ActivePageIndex := HlpPrevPageIndex;
187
    HelpTabsheet.TabVisible := false;
2 daniel-mar 188
  end;
13 daniel-mar 189
 
190
  // Dirty hack...
191
  SrcRep._FindDialog.CloseDialog;
192
  SrcRep._ReplaceDialog.CloseDialog;
2 daniel-mar 193
end;
194
 
13 daniel-mar 195
procedure TForm1.ActionFindExecute(Sender: TObject);
196
begin
197
  SrcRep.FindExecute;
198
end;
199
 
16 daniel-mar 200
var
201
  firstTimeBrowserLoad: boolean = true;
2 daniel-mar 202
procedure TForm1.Run(Sender: TObject);
16 daniel-mar 203
var
204
  bakTS: TTabSheet;
2 daniel-mar 205
begin
5 daniel-mar 206
  memo2.Lines.Text := '';
16 daniel-mar 207
 
208
  if firstTimeBrowserLoad then
209
  begin
210
    bakTS := PageControl1.ActivePage;
211
    try
212
      PageControl1.ActivePage := HtmlTabSheet; // Required for the first time, otherwise, WebBrowser1.Clear will hang
213
      Webbrowser1.Clear;
214
    finally
215
      PageControl1.ActivePage := bakTS;
216
    end;
217
    firstTimeBrowserLoad := false;
218
  end
219
  else
220
    Webbrowser1.Clear;
221
 
5 daniel-mar 222
  Screen.Cursor := crHourGlass;
223
  Application.ProcessMessages;
224
 
225
  try
226
    SynEdit1.Lines.SaveToFile(GetScrapFile);
227
 
8 daniel-mar 228
    memo2.Lines.Text := RunPHPScript(GetScrapFile);
5 daniel-mar 229
 
8 daniel-mar 230
    Webbrowser1.LoadHTML(MarkUpLineReference(memo2.Lines.Text), GetScrapFile);
5 daniel-mar 231
 
232
    if IsTextHTML(memo2.lines.text) then
233
      PageControl1.ActivePage := HtmlTabSheet
234
    else
235
      PageControl1.ActivePage := PlaintextTabSheet;
236
  finally
237
    Screen.Cursor := crDefault;
2 daniel-mar 238
  end;
5 daniel-mar 239
end;
2 daniel-mar 240
 
13 daniel-mar 241
procedure TForm1.SynEdit1MouseWheelDown(Sender: TObject; Shift: TShiftState;
242
  MousePos: TPoint; var Handled: Boolean);
243
begin
244
  if ssCtrl in Shift then
245
  begin
246
    SynEdit1.Font.Size := Max(SynEdit1.Font.Size - 1, 5);
247
  end;
248
end;
249
 
250
procedure TForm1.SynEdit1MouseWheelUp(Sender: TObject; Shift: TShiftState;
251
  MousePos: TPoint; var Handled: Boolean);
252
begin
253
  if ssCtrl in Shift then
254
  begin
255
    SynEdit1.Font.Size := SynEdit1.Font.Size + 1;
256
  end;
257
end;
258
 
5 daniel-mar 259
procedure TForm1.SynEditFocusTimerTimer(Sender: TObject);
260
begin
261
  SynEditFocusTimer.Enabled := false;
262
  Button1.SetFocus; // Workaround for weird bug... This (and the timer) is necessary to get the focus to SynEdit1
263
  SynEdit1.SetFocus;
264
end;
2 daniel-mar 265
 
5 daniel-mar 266
procedure TForm1.WebBrowser1BeforeNavigate2(ASender: TObject;
267
  const pDisp: IDispatch; const URL, Flags, TargetFrameName, PostData,
268
  Headers: OleVariant; var Cancel: WordBool);
269
var
8 daniel-mar 270
  s, myURL: string;
5 daniel-mar 271
  lineno: integer;
7 daniel-mar 272
  p: integer;
5 daniel-mar 273
begin
7 daniel-mar 274
  {$REGION 'Line number references (PHP errors and warnings)'}
8 daniel-mar 275
  if Copy(URL, 1, length(FASTPHP_GOTO_URI_PREFIX)) = FASTPHP_GOTO_URI_PREFIX then
5 daniel-mar 276
  begin
277
    try
8 daniel-mar 278
      s := copy(URL, length(FASTPHP_GOTO_URI_PREFIX)+1, 99);
5 daniel-mar 279
      if not TryStrToInt(s, lineno) then exit;
280
      GotoLineNo(lineno);
281
      SynEditFocusTimer.Enabled := true;
282
    finally
283
      Cancel := true;
284
    end;
8 daniel-mar 285
    Exit;
5 daniel-mar 286
  end;
7 daniel-mar 287
  {$ENDREGION}
288
 
8 daniel-mar 289
  {$REGION 'Intelligent browser (executes PHP scripts)'}
7 daniel-mar 290
  if URL <> 'about:blank' then
291
  begin
292
    myUrl := URL;
293
 
8 daniel-mar 294
    p := Pos('?', myUrl);
295
    if p >= 1 then myURL := copy(myURL, 1, p-1);
7 daniel-mar 296
 
8 daniel-mar 297
    // TODO: myURL urldecode
298
    // TODO: maybe we could even open that file in the editor!
7 daniel-mar 299
 
8 daniel-mar 300
    if FileExists(myURL) and (EndsText('.php', myURL) or EndsText('.php3', myURL) or EndsText('.php4', myURL) or EndsText('.php5', myURL) or EndsText('.phps', myURL)) then
7 daniel-mar 301
    begin
8 daniel-mar 302
      WebBrowser1.LoadHTML(GetDosOutput('"'+GetPHPExe+'" "'+myURL+'"', ExtractFileDir(Application.ExeName)), myUrl);
7 daniel-mar 303
      Cancel := true;
304
    end;
305
  end;
306
  {$ENDREGION}
5 daniel-mar 307
end;
2 daniel-mar 308
 
309
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
310
begin
13 daniel-mar 311
  FastPHPConfig.WriteInteger('User', 'FontSize', SynEdit1.Font.Size);
2 daniel-mar 312
end;
313
 
15 daniel-mar 314
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
315
var
316
  r: integer;
317
begin
318
  if SynEdit1.Modified then
319
  begin
320
    if ParamStr(1) <> '' then
321
    begin
322
      r := MessageDlg('Do you want to save?', mtConfirmation, mbYesNoCancel, 0);
323
      if r = mrCancel then
324
      begin
325
        CanClose := false;
326
        Exit;
327
      end
328
      else if r = mrYes then
329
      begin
330
        SynEdit1.Lines.SaveToFile(GetScrapFile);
331
        CanClose := true;
332
      end;
333
    end
334
    else
335
    begin
336
      SynEdit1.Lines.SaveToFile(GetScrapFile);
337
      CanClose := true;
338
    end;
339
  end;
340
end;
341
 
2 daniel-mar 342
procedure TForm1.FormCreate(Sender: TObject);
343
begin
344
  HlpPrevPageIndex := -1;
345
  CurSearchTerm := '';
13 daniel-mar 346
  Caption := Caption + ' - ' + GetScrapFile;
347
  SrcRep := TFindReplace.Create(self);
348
  SrcRep.Editor := SynEdit1;
2 daniel-mar 349
end;
350
 
351
procedure TForm1.FormDestroy(Sender: TObject);
352
begin
353
  if Assigned(ChmIndex) then
354
  begin
355
    FreeAndNil(ChmIndex);
356
  end;
13 daniel-mar 357
  FreeAndNil(SrcRep);
2 daniel-mar 358
end;
359
 
360
procedure TForm1.FormShow(Sender: TObject);
361
var
362
  ScrapFile: string;
363
begin
364
  ScrapFile := GetScrapFile;
365
  if ScrapFile = '' then
366
  begin
10 daniel-mar 367
    Application.Terminate; // Close;
2 daniel-mar 368
    exit;
369
  end;
15 daniel-mar 370
  if FileExists(ScrapFile) then
371
    SynEdit1.Lines.LoadFromFile(ScrapFile)
372
  else
373
    SynEdit1.Lines.Clear;
2 daniel-mar 374
 
375
  PageControl1.ActivePage := PlaintextTabSheet;
376
 
20 daniel-mar 377
  PageControl2.ActivePage := CodeTabsheet;
2 daniel-mar 378
  HelpTabsheet.TabVisible := false;
5 daniel-mar 379
 
13 daniel-mar 380
  SynEdit1.Font.Size := FastPHPConfig.ReadInteger('User', 'FontSize', SynEdit1.Font.Size);
5 daniel-mar 381
  SynEdit1.SetFocus;
2 daniel-mar 382
end;
383
 
384
function TForm1.GetScrapFile: string;
385
begin
19 daniel-mar 386
  if FScrapFile <> '' then exit(FScrapFile);
387
 
15 daniel-mar 388
  if ParamStr(1) <> '' then
13 daniel-mar 389
    result := ParamStr(1)
390
  else
391
    result := FastPHPConfig.ReadString('Paths', 'ScrapFile', '');
2 daniel-mar 392
  if not FileExists(result) then
393
  begin
19 daniel-mar 394
    repeat
395
      if not OpenDialog3.Execute then
396
      begin
397
        Application.Terminate;
398
        exit('');
399
      end;
2 daniel-mar 400
 
19 daniel-mar 401
      if not DirectoryExists(ExtractFilePath(OpenDialog3.FileName)) then
402
      begin
403
        ShowMessage('Path does not exist! Please try again.');
404
      end
405
      else
406
      begin
407
        result := OpenDialog3.FileName;
408
      end;
409
    until result <> '';
2 daniel-mar 410
 
4 daniel-mar 411
    SynEdit1.Lines.Clear;
412
    SynEdit1.Lines.SaveToFile(result);
2 daniel-mar 413
 
414
    FastPHPConfig.WriteString('Paths', 'ScrapFile', result);
19 daniel-mar 415
    FScrapFile := result;
2 daniel-mar 416
  end;
417
end;
418
 
419
procedure TForm1.Help;
420
var
19 daniel-mar 421
  IndexFile, chmFile, w, OriginalWord, url: string;
2 daniel-mar 422
  internalHtmlFile: string;
423
begin
424
  if not Assigned(ChmIndex) then
425
  begin
426
    IndexFile := FastPHPConfig.ReadString('Paths', 'HelpIndex', '');
427
    IndexFile := ChangeFileExt(IndexFile, '.ini'); // Just to be sure. Maybe someone wrote manually the ".chm" file in there
428
    if FileExists(IndexFile) then
429
    begin
430
      ChmIndex := TMemIniFile.Create(IndexFile);
431
    end;
432
  end;
433
 
434
  if Assigned(ChmIndex) then
435
  begin
436
    IndexFile := FastPHPConfig.ReadString('Paths', 'HelpIndex', '');
437
    // We don't check if IndexFile still exists. It is not important since we have ChmIndex pre-loaded in memory
438
 
439
    chmFile := ChangeFileExt(IndexFile, '.chm');
440
    if not FileExists(chmFile) then
441
    begin
442
      FreeAndNil(ChmIndex);
443
    end;
444
  end;
445
 
446
  if not Assigned(ChmIndex) then
447
  begin
448
    if not OpenDialog1.Execute then exit;
449
 
450
    chmFile := OpenDialog1.FileName;
451
    if not FileExists(chmFile) then exit;
452
 
453
    IndexFile := ChangeFileExt(chmFile, '.ini');
454
 
455
    if not FileExists(IndexFile) then
456
    begin
457
      Panel1.Align := alClient;
458
      Panel1.Visible := true;
459
      Panel1.BringToFront;
460
      Screen.Cursor := crHourGlass;
461
      Application.ProcessMessages;
462
      try
463
        if not ParseCHM(chmFile) then
464
        begin
465
          ShowMessage('The CHM file is not a valid PHP documentation. Cannot use help.');
466
          exit;
467
        end;
468
      finally
469
        Screen.Cursor := crDefault;
470
        Panel1.Visible := false;
471
      end;
472
 
473
      if not FileExists(IndexFile) then
474
      begin
475
        ShowMessage('Unknown error. Cannot use help.');
476
        exit;
477
      end;
478
    end;
479
 
480
    FastPHPConfig.WriteString('Paths', 'HelpIndex', IndexFile);
481
    FastPHPConfig.UpdateFile;
482
 
483
    ChmIndex := TMemIniFile.Create(IndexFile);
484
  end;
485
 
4 daniel-mar 486
  w := GetWordUnderCaret(SynEdit1);
2 daniel-mar 487
  if w = '' then exit;
8 daniel-mar 488
  if CharInSet(w[1], ['0'..'9']) then exit;
19 daniel-mar 489
 
490
  Originalword := w;
491
//  w := StringReplace(w, '_', '-', [rfReplaceAll]);
2 daniel-mar 492
  w := LowerCase(w);
493
  CurSearchTerm := w;
494
 
495
  internalHtmlFile := ChmIndex.ReadString('_HelpWords_', CurSearchTerm, '');
496
  if internalHtmlFile = '' then
497
  begin
498
    HelpTabsheet.TabVisible := false;
499
    HlpPrevPageIndex := -1;
19 daniel-mar 500
    ShowMessageFmt('No help for "%s" available', [Originalword]);
2 daniel-mar 501
    Exit;
502
  end;
503
 
504
  url := 'mk:@MSITStore:'+ChmFile+'::'+internalHtmlFile;
505
 
506
  HlpPrevPageIndex := PageControl2.ActivePageIndex; // Return by pressing ESC
507
  HelpTabsheet.TabVisible := true;
508
  PageControl2.ActivePage := HelpTabsheet;
8 daniel-mar 509
  WebBrowser2.Navigate(url);
510
  WebBrowser2.Wait;
2 daniel-mar 511
end;
512
 
5 daniel-mar 513
procedure TForm1.GotoLineNo(LineNo:integer);
514
var
515
  line: string;
516
  i: integer;
2 daniel-mar 517
begin
5 daniel-mar 518
  SynEdit1.GotoLineAndCenter(LineNo);
519
 
520
  // Skip indent
521
  line := SynEdit1.Lines[SynEdit1.CaretY];
522
  for i := 1 to Length(line) do
523
  begin
8 daniel-mar 524
    if not CharInSet(line[i], [' ', #9]) then
5 daniel-mar 525
    begin
526
      SynEdit1.CaretX := i-1;
527
      break;
528
    end;
529
  end;
530
 
20 daniel-mar 531
  PageControl2.ActivePage := CodeTabsheet;
5 daniel-mar 532
  if SynEdit1.CanFocus then SynEdit1.SetFocus;
2 daniel-mar 533
end;
534
 
8 daniel-mar 535
procedure TForm1.PageControl2Changing(Sender: TObject;
536
  var AllowChange: Boolean);
537
begin
538
  if PageControl2.ActivePage = HelpTabsheet then
539
    HlpPrevPageIndex := -1
540
  else
541
    HlpPrevPageIndex := PageControl2.ActivePageIndex;
542
 
543
  AllowChange := true;
544
end;
545
 
5 daniel-mar 546
procedure TForm1.Memo2DblClick(Sender: TObject);
547
var
16 daniel-mar 548
  pfx, line: string;
5 daniel-mar 549
  p, lineno: integer;
550
begin
551
  line := memo2.Lines.Strings[Memo2.CaretPos.Y];
16 daniel-mar 552
 
18 daniel-mar 553
  {$REGION 'Possibility 1: filename.php:lineno'}
16 daniel-mar 554
  pfx := ExtractFileName(GetScrapFile)+':';
555
  p := Pos(pfx, line);
556
  if p <> 0 then
557
  begin
558
    line := copy(line, p+length(pfx), 99);
559
    if not TryStrToInt(line, lineno) then exit;
560
    GotoLineNo(lineno);
561
  end;
18 daniel-mar 562
  {$ENDREGION}
16 daniel-mar 563
 
18 daniel-mar 564
  {$REGION 'Possibility 2: on line xx'}
16 daniel-mar 565
  pfx := ' on line ';
566
  p := Pos(pfx, line);
567
  if p <> 0 then
568
  begin
569
    line := copy(line, p+length(pfx), 99);
570
    if not TryStrToInt(line, lineno) then exit;
571
    GotoLineNo(lineno);
572
  end;
18 daniel-mar 573
  {$ENDREGION}
5 daniel-mar 574
end;
575
 
17 daniel-mar 576
procedure TForm1.Memo2KeyDown(Sender: TObject; var Key: Word;
577
  Shift: TShiftState);
578
begin
579
  if ((ssCtrl in Shift) and (Key = 65)) then TMemo(Sender).SelectAll;
580
end;
581
 
5 daniel-mar 582
function TForm1.MarkUpLineReference(cont: string): string;
583
var
584
  p, a, b: integer;
585
  num: integer;
586
  insert_a, insert_b: string;
18 daniel-mar 587
 
588
  procedure _process(toFind: string);
5 daniel-mar 589
  begin
18 daniel-mar 590
    p := Pos(toFind, cont);
591
    while p >= 1 do
5 daniel-mar 592
    begin
18 daniel-mar 593
      a := p+1;
594
      b := p+length(toFind);
595
      num := 0;
596
      while CharInSet(cont[b], ['0'..'9']) do
597
      begin
598
        num := num*10 + StrToInt(cont[b]);
599
        inc(b);
600
      end;
5 daniel-mar 601
 
18 daniel-mar 602
      insert_b := '</a>';
603
      insert_a := '<a href="'+FASTPHP_GOTO_URI_PREFIX+IntToStr(num)+'">';
5 daniel-mar 604
 
18 daniel-mar 605
      insert(insert_b, cont, b);
606
      insert(insert_a, cont, a);
5 daniel-mar 607
 
18 daniel-mar 608
      p := b + Length(insert_a) + Length(insert_b);
5 daniel-mar 609
 
18 daniel-mar 610
      p := PosEx(toFind, cont, p+1);
611
    end;
5 daniel-mar 612
  end;
18 daniel-mar 613
begin
614
  {$REGION 'Possibility 1: filename.php:lineno'}
615
  _process(ExtractFileName(GetScrapFile)+':');
616
  {$ENDREGION}
5 daniel-mar 617
 
18 daniel-mar 618
  {$REGION 'Possibility 2: on line xx'}
619
  // TODO: make it more specific to PHP error messages. "on line" is too broad.
620
  _process(' on line ');
621
  {$ENDREGION}
622
 
5 daniel-mar 623
  result := cont;
624
end;
625
 
2 daniel-mar 626
end.