Isopod / tree-sitter-pascal

Treesitter grammar for Pascal and its dialects (Delphi, Freepascal)
MIT License
37 stars 14 forks source link

Pasdoc (and Help Insight) comments support #8

Open overanalytcl opened 1 week ago

overanalytcl commented 1 week ago

Hi! I wanted to come by and say I really like your work.

With that being said, I noticed this doesn't have support for Pasdoc, which is:

a documentation tool for the Object Pascal code (as implemented by FreePascal and Delphi). Documentation is generated from comments found in the source code and from external files.

It is used by various projects such as Castle Game Engine (this tool was made by its creator, Michalis Kamburelis), Synapse, SynEdit, PasDoc itself of course, Allegro.pas, Brook and FBLib.

Specifically, it supports two syntaxes: the Help Insight format used most commonly on Delphi (which is just XMLDoc):

/// <summary>parses the commandline</summary>
/// <param name="CmdLine"> is a string giving the commandline.
/// NOTE: Do not pass System.CmdLine since it contains the
/// program's name as the first "parameter".
/// If you want to parse the commandline as passed by
/// windows, call the overloaded Parse method without
/// parameters. It handles this.</param>
procedure Parse(const _CmdLine: string);

and a JavaDoc-esque format, which is the main way:

{ In this game, there are various types of items:
  @definitionList(
    @itemLabel(Usable, always)
    @item(Typical items that are used. Scrolls, potions, books etc.
      You should override this method to define what
      happens when player uses this item.)

    @itemLabel(Usable, but only when equipped)
    @item(Items that can be used but must be equipped first. E.g.
      magic staffs. You should override this method and check for
      @link(IsEquipped) inside.)

    @itemLabel(Not usable)
    @item(Items that can't be used.
      Don't override this method, default implementation in this
      class will raise an exception.)
  )
}
procedure Use;
{ @abstract(Test of highlighting in @@longCode some things.)

  See
  [https://sourceforge.net/tracker/?func=detail&atid=354213&aid=1422011&group_id=4213]

  @longCode(#
  TOptions = class(TForm)
  private
    procedure WMSysCmd(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  ...
  end;
  #)

  See https://github.com/pasdoc/pasdoc/issues/123 :

  @longcode(#
  Somethins.Name:=1;
  I:=123;
  R:=0.001;
  R:=1E10;
  R:=R*0.99 - 9E-4;
  #)
}
    { Right and top coordinates of the rectangle.
      @code(Right) is simply the @code(Left + Width),
      @code(Top) is simply the @code(Bottom + Height).

      If you use this for drawing, note that the pixel
      with coordinates @code((Right, Top)) is actually *outside*
      of the rectangle (by 1 pixel). That's because the rectangle starts at
      pixel @code((Left, Bottom)) and spans the @code((Width, Height)) pixels.
      @groupBegin }
    property Right: Integer read GetRight;
    property Top: Integer read GetTop;
    { @groupEnd }

    { Return rectangle with given width and height centered
      in the middle of this rectangle. The given W, H may be smaller or larger
      than this rectangle sizes. }
    function CenterInside(const W, H: Cardinal): TRectangle;

    { Grow (when Delta > 0) or shrink (when Delta < 0)
      the rectangle, returning new value.
      This adds a margin of Delta pixels around all sides of the rectangle,
      so in total width grows by 2 * Delta, and the same for height.
      In case of shrinking, we protect from shrinking too much:
      the resulting width or height is set to zero (which makes a valid
      and empty rectangle) if shrinking too much. }
    function Grow(const Delta: Integer): TRectangle; overload;
    function Grow(const DeltaX, DeltaY: Integer): TRectangle; overload;

    { Returns the rectangle with a number of pixels from given
      side removed. Returns an empty rectangle if you try to remove too much.
      @groupBegin }
    function RemoveLeft(W: Cardinal): TRectangle;
    function RemoveBottom(H: Cardinal): TRectangle;
    function RemoveRight(W: Cardinal): TRectangle;
    function RemoveTop(H: Cardinal): TRectangle;
    { @groupEnd }

    { Returns the rectangle with a number of pixels on given
      side added.
      @groupBegin }
    function GrowLeft(const W: Cardinal): TRectangle;
    function GrowBottom(const H: Cardinal): TRectangle;
    function GrowRight(const W: Cardinal): TRectangle;
    function GrowTop(const H: Cardinal): TRectangle;
    { @groupEnd }

    { Scale rectangle position and size around the (0,0) point.

      Since the scaling is independent in each axis,
      this handles "carefully" a half-empty rectangles
      (when one size is <= 0, but other is > 0).
      It scales correctly the positive dimension
      (not just returns @link(Empty) constant),
      leaving the other dimension (it's position and size) untouched.

      These details matter, e.g. when you set @link(TCastleUserInterface.Width), but not
      @link(TCastleUserInterface.Height),
      and then you expect the @link(TCastleUserInterface.EffectiveWidth) to work.
    }
    function ScaleAround0(const Factor: Single): TRectangle;

    { Common part of the two rectangles. }
    class operator {$ifdef FPC}*{$else}Multiply{$endif} (const R1, R2: TRectangle): TRectangle;

    function Equals(const R: TRectangle): Boolean;
  end;

(the last code snippet is taken from Castle Game Engine's source code, it is partially reproduced for size, to get the point across).

The parens are optional, but help with grouping.

Thus, I imagine support for these documentation languages could be achieved using injected languages. Help Insight comments always begin with 3 slashes, and that's where one could simply inject the XML parser. I am not that sure about the JavaDoc-esque comments (and especially @longcode, which would necessitate injecting the Pascal TS grammar into a comment). How would you approach this? Because I am not sure if this is something that downstream should support (i.e. nvim-treesitter, *-ts-mode on Emacs etc.) or if this requires upstream support.