Lazarus

Using the Lazarus IDE => Editor => Topic started by: 440bx on June 05, 2025, 05:59:08 am

Title: Macro language functions
Post by: 440bx on June 05, 2025, 05:59:08 am
Hello,

I'm getting my fingers wet using the editor's macro facility.  To that effect, I've been studying the macros published in the Wiki and focusing on the " Column Align Source" macro.

That macro uses functions such as "pos" and "copy".  Some functions are mentioned in the Wiki but, I didn't see any reference to those two functions.

My question is: is there, somewhere, a complete list of functions, like pos and copy, that can be used in a macro ?  It would be really nice if the list included a description of what the function does (though, in some cases it is obvious but, it may not be in others.)

if the answer is, "look at the source code", please point out the source files I should look into in order to get everything.

Also, any suggestions as to how to debug a macro would be most welcome.

Thank you for your help.
Title: Re: Macro language functions
Post by: Martin_fr on June 05, 2025, 10:30:04 am
copy and pos are part of pascal script. I don't know if that has a documentation... But there isn't any list otherwise. I don't know where in the pascal script sources one would have to look for them.

Title: Re: Macro language functions
Post by: Martin_fr on June 05, 2025, 10:54:15 am
The list of ec* commands can be found here:
https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/ideintf/idecommands.pas#L58
https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/synedit/syneditkeycmds.pp#L68

That is the list as in 4.99

Not all of them are suitable, even if the macro would execute them.
- debugger commands, such as step and similar: They will trigger the command. But they will not wait for it to end. So 2 or more ecStep... will only execute the first. The others will hit the debugger while it is still running the step, and they will be ignored.
- ecNextEditor or any change of editor. They may change the focus in the IDE (not tested, not guaranteed). But the macro will continue in the same editor in which it was started. There is no access to other editors.

Commands that open prompts (such as search) will open the prompt, so the macro becomes interactive.



Other commands are documented here https://wiki.freepascal.org/Editor_Macros_PascalScript

Macro management is here https://wiki.freepascal.org/IDE_Window:_Editor_Macros

For PascalScript itself I don't know.
Title: Re: Macro language functions
Post by: 440bx on June 05, 2025, 12:20:47 pm
Thank you very much Martin.

The macros I needs are just to manipulate text, I'm hoping that because of that, I won't run into the subtleties associated with debugger commands and/or other commands that are not carried out by the script itself.

What follows is quite likely a very dumb question but, here it goes anyway:  I tried putting writeln in the macro code to inspect the vaue of variables the macro is/was using.  The macro interpreter didn't like the writeln at all.  My question is: when I want to verify that some macro variable has the value I want it to have, how do I do that given that writeln isn't available ?

Another question: if I wanted to add lines at the end of the current file, how do I do that ?  I know about Caller.Lines and LineCount but, if I have a line in a string, how do I add/append that string to the Lines array ?

Thank you again for your help and, apologies for such noobie questions.

Just for the record, I believe I'm going to have a lot of questions.  To make up for the time you and others may invest in educating me (along with my own self education), I figured that, at the end of the whole experience, I would write a tutorial that would answer all the questions I had to, hopefully, make things easier for a "macro-newcomer".


Title: Re: Macro language functions
Post by: Martin_fr on June 05, 2025, 12:54:29 pm
I tried putting writeln in the macro code to inspect the vaue of variables the macro is/was using.  The macro interpreter didn't like the writeln at all.  My question is: when I want to verify that some macro variable has the value I want it to have, how do I do that given that writeln isn't available ?
Actually good question.

There isn't a debugger (yet). Pascalscript has features that allow adding one, but - as usual - it's buried on a todo list.
There also isn't a "debugln". Though that could be done relatively easy (at least if it only takes strings, or maybe numbers).

There are 2 ideas I can offer.
  Clipboard.AsText := ...
  Caller.TextBetweenPoints // insert at some fixed pos

Quote
Another question: if I wanted to add lines at the end of the current file, how do I do that ?  I know about Caller.Lines and LineCount but, if I have a line in a string, how do I add/append that string to the Lines array ?
Actually "Lines" is read only. Writing to it would break the IDE's undo capability, so its readonly for technical reasons.

p: TPoint;
p.y := caller.LinesCount;
p.x := length(caller.lines[p.y])+1;
caller.TextBetweenPoints[p,p] := #13#10+thetext;

You should read the section on logical vs physical
https://wiki.freepascal.org/SynEdit#Logical,_Physical_or_Viewed_caret_position

- In macros IIRC you will mostly use logical.
- Macros aren't aware of wrapping (yet?), so "viewed" is not used for macros.
  (I am not sure it is needed, because macros care about content, not on screen representation / of course macros could be extended one day to allow scrolling something into view, then...).



Pascal script macros can define functions. So you can write an
  procedure Append(s: string);

But, you must copy it into each macro that needs it. There is no concept of importing other units/macros in a "uses" like way.

Quote
To make up for the time you and others may invest in educating me (along with my own self education), I figured that, at the end of the whole experience, I would write a tutorial that would answer all the questions I had to, hopefully, make things easier for a "macro-newcomer".
That will be welcome.
Title: Re: Macro language functions
Post by: 440bx on June 05, 2025, 01:45:59 pm
Thank you Martin, this is great.

I already have questions but,  I'm going to apply the ideas you provided and do the reading you mentioned I should do.

I'll be back ;)
Title: Re: Macro language functions
Post by: 440bx on June 05, 2025, 02:54:06 pm
I still have a long ways to go before I have acquired the knowledge to write the macro(s) I want but, there is a simple thing I figured out that can help anyone who's writing a macro.

PascalScript supports IntToStr which combined with ShowMessage provides a workable substitute for writeln.  This can help a great deal in the debugging of a macro.  For instance:
Code: Pascal  [Select][+][-]
  1.   ShowMessage('X: ' + IntToStr(OriginalCaret.X) +
  2.               'Y: ' + IntToStr(OriginalCaret.Y));
  3.  
where OriginalCaret  is a TPOINT that, in this case happens to record where the caret was when the macro started running, is displayed by ShowMessage.

Unlike writeln, there is no compiler magic for ShowMessage to support multiple parameters of different types, for this reason, all parameters have to be strings and they must be "added" together into a single string.

Having a way of inspecting variable values while the macro is running makes it much easier to get it working right.
Title: Re: Macro language functions
Post by: DomingoGP on June 05, 2025, 03:37:48 pm
A little more information to complete what Martin said.

This link contains some help on PascalScript, functions, reserved words and statements.

https://www.elrasoft.com/uuprogs_help/HTML/innerfuse_pascal_script.htm (https://www.elrasoft.com/uuprogs_help/HTML/innerfuse_pascal_script.htm)


Here are the source code that register the standar procedures/functions

https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/PascalScript/Source/uPSRuntime.pas#L9340-9435 (https://gitlab.com/freepascal.org/lazarus/lazarus/-/blob/main/components/PascalScript/Source/uPSRuntime.pas#L9340-9435)

see :procedure TPSExec.RegisterStandardProcs;  from  source line 9340.

Title: Re: Macro language functions
Post by: 440bx on June 05, 2025, 04:26:20 pm
@DomingoGP,

That is very useful information, thank you very much. Much appreciated.
Title: Re: Macro language functions
Post by: Thaddy on June 05, 2025, 04:30:20 pm
At least under Windows, OutputDebugString ends up in the Event log debug window. That is maybe a better alternative than ShowMessage?
I have to test if Lazarus has an equivalent for OutputDebugString on Linux, but it should be something like:
Code: Pascal  [Select][+][-]
  1. {$ifdef unix}
  2. procedure OutputDebugString(const s:string);inline;
  3. begin
  4.   writeln(ErrOutput, s);
  5. end;
  6. {$endif}
Windows tested. Linux - the above code - not yet.
I prefer assertions, though.
Title: Re: Macro language functions
Post by: Martin_fr on June 05, 2025, 04:42:44 pm
At least under Windows, OutputDebugString ends up in the Event log debug window.

PascalScript? Specifically as provided by "Editor macros"?
Title: Re: Macro language functions
Post by: 440bx on June 05, 2025, 04:46:38 pm
At least under Windows, OutputDebugString ends up in the Event log debug window. That is maybe a better alternative than ShowMessage?
Yes, there would be some advantages to using OutputDebugString instead of ShowMessage but, how do you get PascalScript to use OutputDebugString ?
Title: Re: Macro language functions
Post by: Thaddy on June 05, 2025, 04:55:20 pm
It has been a while, but I think that you can register it to PascalScript.
I have to look that up, but I am pretty sure that can be done.
It should look like this:
Code: Pascal  [Select][+][-]
  1. procedure RegisterDebugOutput(Script: TPSScript);
  2. begin
  3.   { Compiler registration }
  4.   Script.Compiler.AddDelphiFunction(
  5.     'procedure OutputDebugString(const Msg: AnsiString);'
  6.   );
  7.  
  8.   { Register }
  9.   RegisterDelphiFunction(Script.Exec, @ScriptOutputDebugString,
  10.     'OutputDebugString', cdRegister);
  11. end;
  12.  
  13. { Call during initialization }
  14. RegisterDebugOutput(MyPascalScriptComponent);
That is how it used to work in Delphi, but I am pretty sure this also works with the Lazarus version, give or take some naming conventions.
If you can't get his working I have to search my brains and come up with a full example. Code completion may help here.
Title: Re: Macro language functions
Post by: Martin_fr on June 05, 2025, 05:02:34 pm
It has been a while, but I think that you can register it to PascalScript.
Maybe, but the version for Editor Macros does not have it registered. And this thread is about how to debug an editor macro.

So unless you suggest (and specify steps) on how the IDE can be extended for this, then it wont work.

Of course, if something needs to be registered then it can be called anything. E.g. debugln could be registered, and it could be forwarded to the IDE messages window.



Also
At least under Windows, OutputDebugString ends up in the Event log debug window.

It only goes to the event log, if it is called by an app running under the debugger. (even a normal app, run without debugger inside the IDE) would not have this show up in the event log.

PascalScript scripts however do not run at all under the debugger. So even if assumed that the function was registered, if that function was forwarded to the WIN api, then it would not show.

+++ EDIT: I am aware you can attach other listeners, but I am replying to the part that says "in the event log debug window". Other listeners wont get it to that window.
Title: Re: Macro language functions
Post by: Thaddy on June 05, 2025, 05:10:32 pm
I understand that 440bx wants to run under the debugger, hence I gave this code.
I am really not sure if it is an answer, but it works at least under Delphi.
I assume also under Lazarus or FPC.
During debugging you may have to add {$apptype console} in the lpr file.
Title: Re: Macro language functions
Post by: Martin_fr on June 05, 2025, 05:18:35 pm
I understand that 440bx wants to run under the debugger, hence I gave this code.
I am really not sure if it is an answer, but it works at least under Delphi.
I assume also under Lazarus or FPC.

Well, there is no debugger for "Editor macro". That is unless you debug the IDE, but then you will not so much debug your script as you debug the script engine. Which is not what he wants or needs.

Inside the script (that is specifically an "editor macro") there is no such function.

Also an debugger to such a script (as it is a script, not an exe and run by a script engine which is an interpreter) would not normally attach to the Window debug API. So it wouldn't get such messages if they actually were generated.
Title: Re: Macro language functions
Post by: Thaddy on June 05, 2025, 05:33:09 pm
Well, I Implemented this for PerfectView in the 1999's and early 2000's and there it worked. We should be able to make it work for Lazarus, the {$apptype console} will give a console window for the script output anyway.
Script or exe.
Title: Re: Macro language functions
Post by: 440bx on June 05, 2025, 05:40:47 pm
As Martin as already explained, even if it were possible to make Synedit's macro facility use OutputDebugString (which cannot be done without changes to the macro's implementation), the macros are NOT running under a debugger, they are running as part of the IDE, therefore if OutputDebugString was called by the macro, the message would go into the bit bucket because the app (in this case the IDE itself) isn't being debugged.

Yes, PascalScript can be "extended" with external functions but, that implies full access to the PascalScript instance (for it to be customized with functions it makes available) which the macro facility does not provide.  IOW, the functions needed to tell PascalScript to use a particular Windows API are not available within Synedit's macro facility.


Title: Re: Macro language functions
Post by: Martin_fr on June 05, 2025, 06:04:36 pm
Well, I Implemented this for PerfectView in the 1999's and early 2000's and there it worked. We should be able to make it work for Lazarus, the {$apptype console} will give a console window for the script output anyway.
Script or exe.

$apptype  does not apply. The script (macro) is never run on its own. It can only run inside the IDE.

Please read up, what editor macros actually do.

And as said before: Of course any debugln/debugoutput/.... could be added (but has not yet / and that would be a change to the IDE / not something to be put into the macro) in the IDE to be available, and then the IDE could print the received data anywhere. So sure, then it could have a console window. Though  there would be more suitable means in that case.
Title: Re: Macro language functions
Post by: Thaddy on June 05, 2025, 07:01:36 pm
Oh, well, that is all a pity.
TinyPortal © 2005-2018