Jump to content

darnocian

Members
  • Content Count

    111
  • Joined

  • Last visited

  • Days Won

    2

Posts posted by darnocian


  1. 3 hours ago, Edwin Yip said:

    After more thoughts I realized that you can customize the starting/closing tags through function parameters, so that to be more practical.

    Yes, you can do something like the following:

    begin
      var ctx := Template.Context;
      ctx.StartToken := '{{';
      ctx.EndToken := '}}';
      Assert.IsEqual('hello', Template.Eval(ctx, '{{ if true }}hello{{else}}bye{{end}}'));
    end;

     


  2. Yes. I've illustrated option1 to start... as mentioned, I'll follow up on option 2...

     

    personally, I don't think there has to be an absolute rule about where presentation logic is... ideally we want it constrained to the presentation layer where possible... going the option2 route can make the template less pleasant to read, and that is sort of what the helper functions are there to do, besides providing additional bridging.... Also note - the option1 route may be faster from a CPU perspective (just mentioning)...

     

    anyways, I'll present that example in a few days when I get some time too look at it again.


  3. Let say there is some type stuff looking like follows:

    type
      TMenuItem = class;
    
      TMenuItemCollection = class
      private
        FChildren: TObjectList<TMenuItem>;
      public
        constructor Create();
        destructor Destroy; override;
        function AddChild(const AName: string): TMenuItem;
        property Children: TObjectList<TMenuItem> read FChildren;
      end;
    
      TMenuItem = class(TMenuItemCollection)
      private
        FName: string;
        FLast: boolean;
      public
        constructor Create(const AName: string = '');
        property Name: string read FName;
        property Last: boolean read FLast write FLast;
      end;
    
      TMenu = TMenuItem;

    With traditional recursion, you can do the following:

    procedure GetHtmlRecursive(const AMenu: TMenu; const ASB: TStringBuilder); forward; overload;
    
    procedure GetItemHtmlRecursive(const AMenuItem: TMenuItem; const ASB: TStringBuilder);
    var
      LItem: TMenuItem;
    begin
      ASB.Append('<li>');
      ASB.Append(AMenuItem.Name);
      if AMenuItem.Children.Count > 0 then
      begin
        GetHtmlRecursive(AMenuItem, ASB);
      end;
      ASB.Append('</li>');
    end;
    
    procedure GetHtmlRecursive(const AMenu: TMenu; const ASB: TStringBuilder); overload;
    var
      LItem: TMenuItem;
    begin
      if AMenu.Children.Count = 0 then
        exit;
      ASB.Append('<ol>');
      for LItem in AMenu.Children do
      begin
        GetItemHtmlRecursive(LItem, ASB);
      end;
      ASB.Append('</ol>');
    end;
    
    function GetHtmlRecursive(const AMenu: TMenu): string; overload;
    var
      LSB: TStringBuilder;
      LItem: TMenuItem;
    begin
      if AMenu.Children.Count = 0 then
        exit('');
      LSB := TStringBuilder.Create;
      try
        GetHtmlRecursive(AMenu, LSB);
        exit(LSB.ToString);
      finally
        LSB.free;
      end;
    end;

    To test, we can try something like:

    var
      lmenu: TMenu;
      lchild: TMenuItem;
      lchild2: TMenuItem;
      lchild3: TMenuItem;
    begin
      lmenu := TMenu.Create;
      try
        lchild := lmenu.AddChild('a');
        lchild2 := lchild.AddChild('a1');
        lchild3 := lchild2.AddChild('a1a');
        lchild3 := lchild2.AddChild('a1b');
        lchild2 := lchild.AddChild('a2');
        lchild2 := lchild.AddChild('a3');
        lchild := lmenu.AddChild('b');
        lchild2 := lchild.AddChild('b1');
        lchild2 := lchild.AddChild('b2');
        lchild3 := lchild2.AddChild('b2a');
        lchild3 := lchild2.AddChild('b2b');
        lchild2 := lchild.AddChild('b3');
        var
        lstr := GetHtmlRecursive(lmenu);
        writeln(lstr);
      finally
        lmenu.free;
      end;
    end;

    So the above just demonstrates how a menu could be done via delphi code...

     

    So next, we want to try test this from the template... I havn't got to test this for myself with the above code yet, but you should be able to do the following:

    type
    	TMyTemplateData = record
        	Menu : TMenu;
            Data : TData; // whatever
        end;
        
       TTemplateUtils = class
    	public
    		class function RenderMenu(const AMenu: TMenu) : string; static; // call the function above, or just move that in here
    	end;    
        
        
    var
    	LMyData:TMyTemplateData;
        LCtx : TTemplateContext;
     begin
        LMyData.Menu := ... some setup ..;
        LMyData.Data := ... some setup ...;
        LCtx := TTemplateContext.Create;
        LCtx.Functions.addfunctions(TTemplateUtils);
        writeln(Template.Eval(LCtx,'Here is my menu:<br><% RenderMenu(Menu) %>', LMyData);
     end;

     


  4. 22 hours ago, Edwin Yip said:

    Without sample code actually I'm not sure what does "use a stack in the template" as described by @darnocian actually mean. I use nested `for` loops to access the tree structure passed to the template engine.

    recursive functions call themselves and helper functions utilising the normal stack by managing CPU stack registers. It is often easier to do things this way as well, but there is also a scenario where you can have a stack overflow, depending on how much memory is accessible by the stack. From an algorithmic perspective, it is possible to transform functionally recursive functions to utilise a developer managed stacks, which are used to backtrack appropriately. 

     

    last night, I wrote a quick recursive one that I can share. I'm a bit busy now, so might only get to the 'dev stack' bound one over the weekend or next week. Let me know if you want me to share stuff earlier.


  5. The template engine is primarily focused on rendering output where data is provided by variables passed in. It does support calling functions written in Delphi. 

     

    I think you could do two ways:

    1. have a function that does the recursion and returns the relevant html (so complexity is in the function in delphi). 
      • pro: template will appear simpler
      • con: some presentation is offloaded to the function
    2. you can pass the menu structure to the template. you could use a stack in the template, which would allow you to process the structure in a recursive way using a stack, rather than relying on function recursion with a function stack.
      • pro: all presentation is in the template
      • con: the template may look a bit more complex

    I think I prefer option 2.

     

    As the template engine will not know offhand how to create a stack, the  easiest is to have a structure containing menu and stack which is passed to the template. 

    • Like 1

  6. Maybe it is simpler to imagine this way:

    - downloading the content from websites = scraping

    - inspecting that content and extracting something meaningful = parsing

     

    so when you scrape a website, the two concepts work hand in hand. get the content, extract anything meaningful, identify if there are any links, repeat... 

     

    parsing isn't an undocumented subject. it is often taught in courses on compiler theory ... but doesn't necessarily have to relate to compilers... also, the way content can be parsed can vary:

    - you can be formal and parse according to some language's grammatical rule set

    - you can cheat - if you know something about the content, you could do some pos() + copy() to get what you want out of it

    - you could even use regular expressions to extract data based on patterns you may be interested in... e.g. emails or urls can be easily identifiable

     


  7. Thanks Edwin.

    35 minutes ago, Edwin Yip said:

    Like others say, the console IDE extension should get more power by directly embedding cmd.exe or the PowerShell window.

    I did mention it was a POC... and am aiming for the full 'embedded' experience. 😉

     

    There isn't really a 'directly embedding' option from what I've researched, but open to any input on the matter. Essentially there are 2 ways I've read about:

    - the legacy apis using the 'old style' win32 apis - what I've essentially seen happen is the guys hide the actual console that is created, and scrape the console buffer, console size and cursor information, and then redraw it. seems convoluted, but I believe this is what the cmd.exe hosting window does. I read this a few days ago, but can't recall the exact source. (and maybe I'm wrong - I'm open to that too) 😉 

    - the 'new style' using the pseudo terminal apis - as Microsoft has embraced the unix world, the new pseudo terminal apis are aimed at bridging the legacy and unix philosophies... I'm still reading... I started so far creating bindings for some of the missing api functions from windows.pas etc...

     

    I'll probably go down the 'new' way to future proof things, but the current version tickles my immediate itch, but I know there is more scratching on its way.

     

    If there are simpler approaches, I'm open to hearing about them.  There are some 3rd party libs used in other IDEs like WinPty... I have not explored it yet, but also not sure if the additional dependency is really needed, except from a saving time perspective if it proves easy to do and does the job well.

     

    35 minutes ago, Edwin Yip said:

    Maybe it's more convenient to immediately create a console window for the current project, after clicking the Tools menu item.

    i wanted to have a 'list' of consoles, as these windows can go invisible, so something needs to manage them.

     

    I wanted to add a menu item in the right-click 'project structure'... but that menu is already so long...  But maybe on the main 'Project' menu in the IDE, the item could be for the current project. Although that may require an additional popup to select which terminal. I'm not totally happy with my current UI behaviour in  the current implementation as it presents a popup to give the window a name. if I present an option as a menu item, then a may want to have 2 options - start a powershell.exe, start a cmd.exe... the list can just get automatic naming, and people could rename afterwards if they desire.


  8. 21 hours ago, Fr0sT.Brutal said:

    Just an idea... is it possible to inject a console window itself into a dockable panel? Maybe it would be easier than full terminal emulation which would lack many features anyway.

    Agreed. 

     

    I'll keep you posted on the evolution...


  9. just a note when signing files - it may be obvious, but also ensure that any relevant metadata in versioninfo matches the digital signature. e.g. don't use an abbreviation in one place and not the other....the AV guys like metadata to match exactly. Most vendors have a url where you can upload a sample for review where upon acceptance the app will be whitelisted. It is annoying to have to do this however.

     

    I have the false positives kicking in while I'm writing unit tests from time to time.  the test app gets deleted before it has a chance to run. ;( And then making what may be a seemingly irrelevant change gets it to work again.

    • Like 1

  10. 2 hours ago, Stéphane Wierzbicki said:

     Will you considering directly docking cmd or PowerShell Windows ? In your video cmd or PowerShell Windows need to be moved and docked into Delphi.

     

     

    I will definitely try to explore the direct docking when I can.

     

    I did ask David Millington about some of the features exposed via the ToolsAPI and there are some limitations... I think it would be nice for the console windows to be docked alongside the main console manager window, or possibly have some icons that can represent the preferred docking location (much like the alignment popup for controls in the object inspector). 

     

    The dockable windows can be created, shown or hidden, but the ToolsAPI doesn't present other features currently. It may just be that I need to play around more as well. I found an ancient reference by Allen Bauer on the topic - it looked like it could have been the original prototype. But I'll see how that goes...

     

    The first big step I'd like to make is getting full terminal emulation going properly.

     

    Thanks again for your comments.


  11. I've just been working on some IDE experts/wizards. It can be a little annoying ensuring a clean shutdown sometimes, and not always easy to identify the root problem.

     

    I wish the IDE would allow for a registry config variable for displaying/hiding exceptions on shutdown. It is a bit annoying when 3rd parties behave badly. 😉 


  12. Hi everyone,

     

    Following fast on my last post, I thought I'd also share another development I've started  - a Console Manager for the Delphi IDE.

     

    This is a tool that allows you to spin up a cmd.exe or powershell.exe within a dockable form, localised within the Delphi IDE.

     

    Features:

    - It is aware of the active project, so it will open up in the directory in which the dproj is located. It is still very alpha as in it just streams stdout/stdin, and not totally pleasant on the eye, but ok for an alpha POC.

    - I've  got a little slider that allows you to zoom in/out.

    - (planning) The ability to have lists of commands / saved environments that could be applied to a newly created session.

     

    This is a POC, and still have some more work to do as I want to embrace the new pseudo console api which was introduced with the new Windows Terminal drive along with the VT emulation which would support colour, repositioning of the cursor, etc.

     

    I have not released this publicly yet, but attached is a short video demonstrating how it works.

     

    If you are interested in accessing it, please send me a mail: conrad.vermeulen@gmail.com. Let me know about what IDEs versions you may be interested in. I think I can do XE8 onwards.

     

    I'll provide more information when it is officially released.

     

    Regards,

    Conrad

     

     

    • Like 8

  13. Hi everyone,

     

    Last week I made a Debug Configuration Manager for Delphi.

     

    It is a tool to allow for lists of debug configurations (Host App, Parameters, Work Directory, Environment Variables) to be easily managed in the IDE and be applied to an active project. It is also project aware, so the configuration settings are managed and persisted on a per project basis.

     

    I have not released this publicly yet, but attached is a short video demonstrating how it works.

     

    If you are interested in accessing it, please send me a mail: conrad.vermeulen@gmail.com

     

    I'll provide more information when it is officially released.

     

    Regards,

    Conrad

     

     


  14. 11 hours ago, CoMPi74 said:

    This is not the case I presented

    Sorry, I think I was a dufus and didn't pay attention to what i was doing. 😉

     

    Reproducing correctly with event2:TEvent2<T> , the event2 is not exposed as you highlighted...

     

    It would be useful to raise the Quality Portal issue so that this type of scenario works... Generics can be really powerful, but unfortunately there are lots of bugs with the implementation. ;(


  15. I thought I should try properly to try reproduce as well rather than speculate. 😉  The following worked for me:

    type
      TEvent = procedure (avalue:integer)of object;
      TEvent2<T:record> = procedure (avalue:t) of object;
      TCustomEdit<T:record> = class(TEdit)
      private
        fevent1:tevent;
        fevent2:TEvent2<integer>;
          published
    
            property event1:tevent read fevent1 write fevent1;
            property event2:tevent2<integer> read fevent2 write fevent2;
    
      end;
    
      TEditEx = class(TCustomEdit<integer>)
      end;

    image.thumb.png.cd13ea66c678979afa2adda4b48cacc5.png

    ide was fine on my end.
     

     


  16. I think the compiler is getting confused as your generic parameters matches the names of existing types.

     

    if you rename the standalone IMyIntf to something else, then the generic type can remain TMyList<IMyIntf>, and similarly for TMyType, the generic types should be ok.

     

    alternatively rename the generic parameters, and even placing constraints may be helpful. e.g.

    TMyList<TIntf: IMyInf> = class 

    ...

    TMyTypeList<T:class> = class  

    ...


  17. 9 hours ago, Fr0sT.Brutal said:

    Btw RTL has a TParser class but it's intended for reading DFM files. Not a something reusable

    I wasn’t sure if you were referring to the test snippets I posted? in the project I made to illustrate writing the custom parsers,  I provided alternative TParser implementations - just thought I’d clarify in case of confusion.


  18. 1 hour ago, Fr0sT.Brutal said:

    Sometimes you don't even need to write a parser - just define the grammar and generate parser code from it. Alas, I'm not aware of such possibility for Delphi.

    Do you mean lexer & parser generators like lex&yacc/antlr? Antrl3 has some Delphi support, but Antlr4 hasn't been ported yet.  FPC has a TP Lex and TP Yacc - the tools are still available, but the documentation seems to have gone offline. I tried that a while back, but there were too much global state for my liking.

×