9

I'm creating a built-in script engine using PascalScript from RemObjects (excellent) and the SynEdit editor. It's almost finished using the IDE example shipped with PascalScript and the IDE example in SynEdit - but - I can't see how to ask PascalScript whether a numbered source line is 'executable' or not. (I can then use this to mark the SynEdit gutter with the 'Delphi blue dot'). I guess I might have to do a dissassembly of the ROPS output?

Any PascalScript experts here? THanks. Brian.

Brian Frost
  • 13,334
  • 11
  • 80
  • 154
  • This looks like an interesting project. Do you have a site for it? Will you make the source available once you get it working? – Mason Wheeler Jun 26 '09 at 22:16

3 Answers3

9

Have a look at the source code of Inno Setup. It does show a small dot in the SynEdit gutter area for lines with executable code, gray ones for lines that are executable but have not been executed, green ones for lines that have been hit at least once.

The code for this can be found in CompForm.pas, look for the TLineState type. The information is set up in the iscbNotifySuccess state of the compiler callback, you could do the same in your IDE. You may need to adapt the code to handle multiple source files, as the Inno Setup compiler deals with code snippets in the single source file only.

In the Pascal Script sources you should have a look at the TPSCustomDebugExec.TranslatePositionEx() method - it does return the name of the source file as well.

mghie
  • 32,028
  • 6
  • 87
  • 129
1

I don't know exactly how it does it, but the IDE project in the PascalScript package (found under \samples\debug ) is able to offer Step Into and Step Over (F7 and F8) functionality, so logically it has to have some way of associating PS bytecode with lines of script code. Try examining that project to see how it does it. As a bonus, it uses SynEdit too, so the ideas will be easy to adapt to your own system.

Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
  • Thanks for the comment Mason. I'v been using that example a lot. The StepInto and StepOver are built into the TPSDebugScript class and not surprisingly they get their 'I'v ended up at this new line' by actually executing the code. Before the code gets executed, I need to query the script code for possible lines and that's the bit that flumoxing me. Bri – Brian Frost Jun 26 '09 at 21:17
  • How does it map code to source lines? There's not a 1:1 relationship. Consider "x := 5;" and "x := 5 * performCalculation(y, z + x);" Both are a single line, but the second one contains a lot more operations to be performed. There has to be some sort of metadata mixed in with the bytecode that it uses to do the mapping. – Mason Wheeler Jun 26 '09 at 21:26
  • @Mason: Look into TPSCustomDebugExec.TranslatePositionEx(). It has the source position in the debug info records, and for every op it can look up the source position info. For your second example all those operations would return the stored source position of the first operation in that line. – mghie Jun 26 '09 at 21:51
1

I know this is an old question but I have been doing the same thing myself and the suggestions above do not really help. Inno setup for instance does not use Synedit, it uses scintilla editor.

Also the TPSCustomDebugExec.TranslatePositionEx() does the opposite to what is wanted, it gives a source line number from a runtime code position.

After faffing around for some time I came to the conclusion that the easiest way was to add a function to the Pascalscript code.

the new method is added to the TPSCustomDebugExec class in the uPSdebugger unit.

function TPSCustomDebugExec.HasCode(Filename:string; LineNo:integer):boolean;
var i,j:integer; fi:PFunctionInfo; pt:TIfList; r:PPositionData;
begin
  result:=false;
  for i := 0 to FDebugDataForProcs.Count -1 do
  begin
    fi := FDebugDataForProcs[i];
    pt := fi^.FPositionTable;
    for j := 0 to pt.Count -1 do
    begin
      r:=pt[j];
      result:= SameText(r^.FileName,Filename) and (r^.Row=LineNo);
      if result then exit
    end;
  end;
end;

and the paint gutter callback in the main editor form is as below

procedure Teditor.PaintGutterGlyphs(ACanvas:TCanvas; AClip:TRect;
  FirstLine, LastLine: integer);
var a,b:boolean; LH,LH2,X,Y,ImgIndex:integer;
begin
  begin
    FirstLine := Ed.RowToLine(FirstLine);
    LastLine := Ed.RowToLine(LastLine);
    X := 14;
    LH := Ed.LineHeight;
    LH2:=(LH-imglGutterGlyphs.Height) div 2;
    while FirstLine <= LastLine do
    begin
      Y := LH2+LH*(Ed.LineToRow(FirstLine)-Ed.TopLine);
      a:= ce.HasBreakPoint(ce.MainFileName,FirstLine);
      b:= ce.Exec.HasCode(ce.MainFileName,FirstLine);
      if Factiveline=FirstLine then
      begin
        if a then
          ImgIndex := 2   //Blue arrow+red dot (breakpoint and execution point)
        else
          ImgIndex := 1;  //Blue arrow (current line execution point)
      end
      else
        if b then
        begin
          if a then
            ImgIndex := 3  //Valid Breakpoint marker
          else
            ImgIndex := 0; //blue dot  (has code)
        end
        else
        begin
          if a then
            ImgIndex := 4  //Invalid breakpoint (No code on this line)
          else
            ImgIndex := -1; //Empty (No code for line)
        end;
      if ImgIndex >= 0 then
        imglGutterGlyphs.Draw(ACanvas, X,Y,ImgIndex);
      Inc(FirstLine);
    end;
  end;
end;

The Synedit with Line numbers, code dots, breakpoints, bookmarks and execution point look as in the image below

enter image description here

Andy k
  • 1,056
  • 1
  • 11
  • 22