Making an IDE using Pascal Script and SynEdit - delphi

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.

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.

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.

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

Related

How to Change "Table Positioning" in the table properties Inside a MS Word Document Using Delphi XE5

I apologize in advance, This is very confusing to explain. Please assist in making it clearer if need be.
I am working with a MS Word document that i generate from code using the following code. The document has 1 table with a bunch of rows and columns that i intend to populate.
wrdDoc.Tables.Add(wrdSelection.Range,9,2);
wrdDoc.tables.Item(1).Rows.Alignment := wdAlignRowLeft;
wrdDoc.Tables.Item(1).Columns.Item(1).SetWidth(155,wdAdjustNone);
wrdDoc.Tables.Item(1).Columns.Item(2).SetWidth(299,wdAdjustNone);
wrdDoc.tables.Item(1).Borders.Item(wdBorderLeft).LineStyle := wdLineStyleSingle;
wrdDoc.tables.Item(1).Borders.Item(wdBorderRight).LineStyle := wdLineStyleSingle;
wrdDoc.tables.Item(1).Borders.Item(wdBorderVertical).LineStyle := wdLineStyleSingle;
wrdDoc.tables.Item(1).Borders.Item(wdBorderTop).LineStyle := wdLineStyleSingle;
wrdDoc.tables.Item(1).Borders.Item(wdBorderBottom).LineStyle := wdLineStyleSingle;
wrdDoc.tables.Item(1).Borders.Item(wdBorderHorizontal).LineStyle := wdLineStyleSingle;
Basically what i am trying to do is change the following values:
Right Click on the table->Table Properties->Table Tab
Text Wrapping = Around
->Positioning->Horizontal:
Position = -0.18"
Relative To = Margin
->Positioning->Vertical:
Position = -0.63"
Relative To = Paragraph
->Positioning->Options:
Move With Text = True
Allow Overlap = True
I have not been able to find any code to assist me. Or even any Code samples that handle changing the text wrapping to around using code in Delphi. So any assistance would be great.
Thank You
The following code does what you asked using D7 and Word2007.
You don't say whether your unit already uses one of the Delphi import units for the MS Word type libraries. You'll need to use one, because that's where the constants like wdTableLeft are defined. I'm using D7 (+Word 2007), so I used the Word2000 import unit that came with D7.
Also my Table and TablesRows are OleVariants which you'll need to add to your code if you don't declare them already.
First thing is that you'll need to add some code above your procedure which creates the table. The reason for this is explained below.
const
CmToPostScriptPoints : Single = 28.3464567;
InchesToCm : Single = 2.54;
function CentimetersToPoints(Centimeters : Single) : Single;
begin
Result := CmToPostScriptPoints * Centimeters;
end;
Then replace the code in your q by the following. Please read the embedded comments carefully because they explain a couple of problems I ran into which took a long time to figure out.
Table := wrdDoc.Tables.Add(wrdSelection.Range, 9, 2);
TableRows := Table.Rows;
TableRows.WrapAroundText := True;
// TableRows.MoveWithText := True;
// Note: If you un-comment the line above it will cause an exception
// Method "MoveWithText" not supported by automation object
// However, even with MoveWithText commented out, the corresponding property
// in Word's table properties will still be ticked by the time the code is finished
TableRows.AllowOverlap := True;
Table.Rows.Alignment := wdAlignRowLeft;
Table.Columns.Item(1).SetWidth(155,wdAdjustNone);
Table.Columns.Item(2).SetWidth(299,wdAdjustNone);
Table.Borders.Item(wdBorderLeft).LineStyle := wdLineStyleSingle;
Table.Borders.Item(wdBorderRight).LineStyle := wdLineStyleSingle;
Table.Borders.Item(wdBorderVertical).LineStyle := wdLineStyleSingle;
Table.Borders.Item(wdBorderTop).LineStyle := wdLineStyleSingle;
Table.Borders.Item(wdBorderBottom).LineStyle := wdLineStyleSingle;
Table.Borders.Item(wdBorderHorizontal).LineStyle := wdLineStyleSingle;
TableRows.RelativeHorizontalPosition := wdRelativeHorizontalPositionMargin;
TableRows.RelativeVerticalPosition := wdRelativeVerticalPositionParagraph;
TableRows.HorizontalPosition := CentimetersToPoints(-0.18 * InchesToCm) ;
// Note : At first, I thought the line above didn't do anything because
// after this routine finishes, the HorizontalPosition in Word
// under TableProperties | Positioning is shown as Left.
//
// However, if you put a breakpoint on the line above,
// and single-step past it, you should see the table shift leftwards
// when the line is executed. The ShowMessage should display - 0.179[...]
ShowMessage(FloatToStr(TableRows.HorizontalPosition / 72)); // 72 PostScript points to an inch
TableRows.VerticalPosition := CentimetersToPoints(-0.63 * InchesToCm);
The reason I've defined a CentimetersToPoints function rather than the Word Application's CentimetersToPoints is that there seems there is a long-standing problem with trying to call CentimetersToPoints from Delphi code - if you're interested, see this SO q and its comments to the answer:
Unspecified error when calling Word CentimetersToPoints via OLE

How to write and show something on Delphi IDE status bar

I want to know how can I write a module to show something like clock or other thing on Borland Delphi 7 IDE status bar, because I know it's possible but I couldn't find how!
To insert a text in a StatusBar, you have to insert a panel first.
Just select your statusbar, find the property "Panels" (or perform double click over the statusbar) and click in "Add new".
After that, you can write what you want inside the panel in the property "Text" (you can insert one or more panels).
To do it programmatically, you can do something like this:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
StatusBar1.Panels[0].Text := 'Today is: ' + FormatDateTime('dd/mm/yyyy hh:nn:ss', Now);
end;
Since OP didn't replied with more details, I'm going to post a little demonstration how to reach a status bar of Delphi's edit window. I had no success with adding new distinct status panel w/o disturbing layout, so I'm just changing the text of INS/OVR indicator panel.
Disclaimer: I still do not have access to the machine with Delphi 7 installed, so I've done that in BDS ("Galileo") IDE. However, differences should be minor. I believe what main difference lies in the way how we locate edit window.
Key strings are: 'TEditWindow' for edit window class name and 'StatusBar' for TStatusBar control name owned by edit window. These strings are consistent across versions.
{ helper func, see below }
function FindForm(const ClassName: string): TForm;
var
I: Integer;
begin
Result := nil;
for I := 0 to Screen.FormCount - 1 do
begin
if Screen.Forms[I].ClassName = ClassName then
begin
Result := Screen.Forms[I];
Break;
end;
end;
end;
procedure Init;
var
EditWindow: TForm;
StatusBar: TStatusBar;
StatusPanel: TStatusPanel;
begin
EditWindow := FindForm('TEditWindow');
Assert(Assigned(EditWindow), 'no edit window');
StatusBar := EditWindow.FindComponent('StatusBar') as TStatusBar;
(BorlandIDEServices as IOTAMessageServices).AddTitleMessage(Format('StatusBar.Panels.Count = %d', [StatusBar.Panels.Count]));
//StatusPanel := StatusBar.Panels.Add;
StatusPanel := StatusBar.Panels[2];
StatusPanel.Text := 'HAI!';
end;
initialization
Init;
finalization
// nothing to clean up yet
Another note: As you see, I use Open Tools API to output debug messages only, to interact with IDE I do use Native VCL classes. Therefore, this code must be in package.
The code above is a relevant part of the unit which should be contained in package. Do not forget to add ToolsAPI to uses clause as well as other appropriate referenced units (up to you).
Package should require rtl, vcl and designide (important!).
Since I run the testcase directly from initialization section, installing the package is enough for testcase to run and produce some result.

PosEx not works in Design-Time Package

I have created a design-time package for my delphi7 IDE. I want a component on a form offer me some menu items in the context menu during design-time. When i debug the package, i can see that my code gets executed, but when it comes to a line using the built-in function PosEx, the behaviour confuses me. In the debugger, i can see that the text to search in contains the part to search for, but the function returns 0 anyway! And, if i execute this directly (instead of debugging the IDE using it in a project) it all works fine.
This is my code:
function RemoveLeftOverPlaceHolders( var PIO_sTemplate: string);
var nStartPos: integer;
const JPCG_FOREACH_START = '{%foreach ';
begin
nStartPos := 0;
repeat
nStartPos := PosEx( JPCG_FOREACH_START, PIO_sTemplate, nStartPos ); // <-- nStartPos is always 0 !
if nStartPos > 0 then begin
...
end;
until nStartPos = 0;
end;
Is it possible that the string types are changed during execution in the IDE? (any ansistring-unicode problem?)
Edit: I'm using GExperts and DelphiSpeedUp
Stupid... the answer stands right in the code... nStartPos is initialized with 0, whichs leads to a failing PosEx. Strange that it worked when executing the code out of a design pack...

Why do I get "left side cannot be assigned to" for TRect after upgrading Delphi?

I am migrating the code from Delphi 7 to XE2 one of the Graphical module.
we are using TRect variable , the old code is working in Delphi 7 without issue
Ex:
Var
Beold : TRect
begin
Beold.left := Beold.right;
end.
while porting the code to new XE2 we are facing the issue
E0264 : Left side cannot be assigned to
Can you please explain what is the changes in XE2 TRect and D7, how we can assign the valuse
The code you posted compiles and runs fine in a quick Delphi test app, so it's not your real code.
I'd suspect what you've hit is a change in the with statement when it's related to using properties, however. There was a bug in previous versions of Delphi that existed for many years that was finally fixed recently. IIRC, it was first mentioned in a note in the README.HTML file for D2010. It's been added to the documentation in XE2 (not as a behavior change, but the new behavior is documented). The documentation is located here at the docwiki.
(Additional info: It must have been 2010 where it changed; Marco Cantù's Delphi 2010 Handbook mentions it on page 111 as "The With Statement Now Preserves Read-Only Properties" which describes this behavior and the solution I indicated below.)
Instead of accessing the property of a class directly using a with statement, you now need to declare a local variable, and read and write the whole thing directly (error handling omitted for clarity - yes, I know there should be a try..finally block to free the bitmap).
var
R: TRect;
Bmp: TBitmap;
begin
Bmp := TBitmap.Create;
Bmp.Width := 100;
Bmp.Height := 100;
R := Bmp.Canvas.ClipRect;
{ This block will not compile, with the `Left side cannot be assigned to` error
with Bmp.Canvas.ClipRect do
begin
Left := 100;
Right := 100;
end;
}
// The next block compiles fine, because of the local variable being used instead
R := Bmp.Canvas.ClipRect;
with R do
begin
Left := 100;
Right := 100;
end;
Bmp.Canvas.ClipRect := R;
// Do other stuff with bitmap, and free it when you're done.
end.
turns out, using
with (Bmp.Canvas.ClipRect) do
begin
Bottom := 100;
end;
throws error: [Left side cannot be assigned to]
Yet,
with Bmp.Canvas.ClipRect do
begin
Bottom := 100;
end;
does not.
Delphi 10.3.3 is just as finicky about parenthesis as the older versions.

Indenting bullets

It is not bad enough that I am so new to programming; This week I have done more Google searching and less Google finding than I have ever before.
Delphi v7
I have figured out how to create bullets in my richedit control. What I can't find out is how to indent them when the user creates them.
Any ideas?
Set the TRichEdit.Paragraph.FirstIndent. The bullets will be indented by the additional amount of FirstIndent. (You use FirstIndent because a bullet item is the first line of a new paragraph.)
RichEdit1.Paragraph.FirstIndent := RichEdit1.Paragraph.FirstIndent + 10;
Here's a quick demo based on the RichEdit demo that's shipped with Delphi for years. I simply added two new TToolButtons to the ToolBar (the two right-most buttons, named tbLessIndent and tbMoreIndent respectively, with glyphs from the GlyFx images supplied with Delphi), and added the following event handlers to the existing form as the ToolButton.OnClick events:
procedure TMainForm.tbLessIndentClick(Sender: TObject);
begin
Editor.Paragraph.FirstIndent := Editor.Paragraph.FirstIndent - 10;
tbLessIndent.Enabled := Editor.Paragraph.FirstIndent > 9;
end;
procedure TMainForm.tbMoreIndentClick(Sender: TObject);
begin
Editor.Paragraph.FirstIndent := Editor.Paragraph.FirstIndent + 10;
tbLessIndent.Enabled := True;
end;
Here's a sample new document with a few items added:
For more info, see the documentation on Numbering and FirstIndent (XE2 docs, but they're about the same)

Resources