Subversion Repositories fastphp

Rev

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