mirror of
https://github.com/AdaCore/markdown.git
synced 2026-02-12 13:11:15 -08:00
228 lines
6.9 KiB
Ada
228 lines
6.9 KiB
Ada
--
|
|
-- Copyright (C) 2021-2023, AdaCore
|
|
--
|
|
-- SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
--
|
|
|
|
with VSS.Regular_Expressions;
|
|
with VSS.Strings.Conversions;
|
|
with VSS.Strings.Cursors;
|
|
|
|
package body Markdown.Implementation.List_Items is
|
|
|
|
Bullet_List_Marker : constant Wide_Wide_String := "[-+*]";
|
|
|
|
Ordered_List_Marker : constant Wide_Wide_String := "([0-9][0-9]*)[.)]";
|
|
|
|
Marker_Pattern : constant Wide_Wide_String :=
|
|
"^((?: | | |)(" & -- 1 group (indent + marker), 2 group (the
|
|
Bullet_List_Marker & "|" & -- marker it-self)
|
|
Ordered_List_Marker & -- 3 group (inside Ordered_List_Marker)
|
|
")) *";
|
|
|
|
Prefix : VSS.Regular_Expressions.Regular_Expression;
|
|
-- Regexp of Marker_Pattern
|
|
|
|
Indent_1 : VSS.Regular_Expressions.Regular_Expression;
|
|
-- One or more spaces at the beginning of a string: "^ +"
|
|
|
|
function Marker_Value
|
|
(Match : VSS.Regular_Expressions.Regular_Expression_Match) return Natural;
|
|
-- Return numeric marker as an integer
|
|
|
|
----------------------------------
|
|
-- Consume_Continuation_Markers --
|
|
----------------------------------
|
|
|
|
overriding procedure Consume_Continuation_Markers
|
|
(Self : in out List_Item;
|
|
Input : in out Input_Position;
|
|
Ok : out Boolean)
|
|
is
|
|
Match : constant VSS.Regular_Expressions.Regular_Expression_Match :=
|
|
Indent_1.Match (Input.Line.Expanded, Input.First);
|
|
begin
|
|
Ok := Match.Has_Match and then
|
|
Match.Marker.Character_Length >= Self.Marker_Width;
|
|
|
|
if Ok then
|
|
-- We have enough spaces at the beginning, consume them
|
|
Self.Has_Blank_Line := Self.Has_Blank_Line
|
|
or Self.Ends_With_Blank_Line;
|
|
|
|
Self.Ends_With_Blank_Line := False;
|
|
|
|
Forward (Input.First, Self.Marker_Width);
|
|
elsif not Input.First.Has_Element then
|
|
-- We have characters, treat it as an empty line
|
|
Ok := not
|
|
(Self.Ends_With_Blank_Line
|
|
and Self.Starts_With_Blank_Line
|
|
and not Self.First_Line);
|
|
|
|
if Ok then
|
|
Self.Ends_With_Blank_Line := True;
|
|
end if;
|
|
end if;
|
|
|
|
Self.First_Line := False;
|
|
end Consume_Continuation_Markers;
|
|
|
|
------------
|
|
-- Create --
|
|
------------
|
|
|
|
overriding function Create
|
|
(Input : not null access Input_Position) return List_Item
|
|
is
|
|
Match : constant VSS.Regular_Expressions.Regular_Expression_Match :=
|
|
Prefix.Match (Input.Line.Expanded, Input.First);
|
|
|
|
Suffix_Length : constant VSS.Strings.Character_Count :=
|
|
Match.Marker.Character_Length - Match.Marker (1).Character_Length;
|
|
begin
|
|
return Result : List_Item do
|
|
Result.Is_Ordered := Match.Marker (3).Is_Valid;
|
|
Result.Marker := Match.Captured (2);
|
|
Result.Marker_Value := Marker_Value (Match);
|
|
|
|
if Suffix_Length <= 4 and then
|
|
Match.Last_Marker.Character_Index
|
|
= Input.Line.Expanded.Character_Length
|
|
then
|
|
-- Empty line marker: ^\ {0,3}(Marker)(\ {0,4}$).
|
|
Result.Marker_Width := Match.Marker (1).Character_Length + 1;
|
|
Result.First_Line := True;
|
|
Result.Starts_With_Blank_Line := True;
|
|
-- Shift Input.First to end-of-line
|
|
Input.First.Set_After_Last (Input.Line.Expanded);
|
|
|
|
elsif Suffix_Length > 4 then
|
|
-- Indented code in the list item
|
|
|
|
Result.Marker_Width := Match.Marker (1).Character_Length + 1;
|
|
|
|
Input.First.Set_At (Match.Last_Marker (1)); -- at marker end
|
|
-- Skip 1 space after marker
|
|
Forward (Input.First, 2);
|
|
|
|
else
|
|
|
|
Result.Marker_Width := Match.Marker.Character_Length;
|
|
-- Skip marker and all spaces if any
|
|
Input.First.Set_At (Match.Last_Marker);
|
|
Forward (Input.First);
|
|
end if;
|
|
end return;
|
|
end Create;
|
|
|
|
--------------
|
|
-- Detector --
|
|
--------------
|
|
|
|
procedure Detector
|
|
(Input : Input_Position;
|
|
Tag : in out Ada.Tags.Tag;
|
|
CIP : out Can_Interrupt_Paragraph)
|
|
is
|
|
Marker : VSS.Strings.Virtual_String;
|
|
Number : Natural;
|
|
Match : VSS.Regular_Expressions.Regular_Expression_Match;
|
|
|
|
Suffix_Length : VSS.Strings.Character_Count;
|
|
End_Of_Line_Matched : Boolean;
|
|
begin
|
|
Initialize;
|
|
|
|
CIP := True; -- Suppress a warning about uninitialized parameter
|
|
Match := Prefix.Match (Input.Line.Expanded, Input.First);
|
|
|
|
if Match.Has_Match then
|
|
End_Of_Line_Matched := Match.Last_Marker.Character_Index
|
|
= Input.Line.Expanded.Character_Length;
|
|
|
|
Marker := Match.Captured (2);
|
|
|
|
Suffix_Length :=
|
|
Match.Marker.Character_Length - Match.Marker (1).Character_Length;
|
|
|
|
if Marker.Character_Length > 10 then
|
|
-- no more than 9 digits in the marker are allowed
|
|
return;
|
|
elsif Suffix_Length = 0 and not End_Of_Line_Matched then
|
|
-- We have non-space characters just after marker. Not a list item
|
|
return;
|
|
end if;
|
|
|
|
Tag := List_Item'Tag;
|
|
|
|
Number := Marker_Value (Match);
|
|
|
|
-- Calculate Can_Interrupt_Paragraph
|
|
CIP :=
|
|
(if Suffix_Length <= 4 and then End_Of_Line_Matched then
|
|
-- Empty line marker: ^\ {0,3}(Marker)(\ {0,4}$).
|
|
-- An empty list item cannot interrupt a paragraph.
|
|
False
|
|
elsif Match.Marker (3).Is_Valid then
|
|
-- Check if numbered item is `1`
|
|
|
|
Number = 1
|
|
else
|
|
|
|
True);
|
|
end if;
|
|
end Detector;
|
|
|
|
----------------
|
|
-- Initialize --
|
|
----------------
|
|
|
|
procedure Initialize is
|
|
begin
|
|
if not Prefix.Is_Valid then -- Construct Prefix regexp
|
|
Prefix := VSS.Regular_Expressions.To_Regular_Expression
|
|
(VSS.Strings.To_Virtual_String (Marker_Pattern));
|
|
|
|
Indent_1 := VSS.Regular_Expressions.To_Regular_Expression
|
|
(VSS.Strings.To_Virtual_String ("^ *")); -- XXX: Fix with "^ +"
|
|
end if;
|
|
end Initialize;
|
|
|
|
----------------
|
|
-- Is_Ordered --
|
|
----------------
|
|
|
|
function Is_Ordered (Self : List_Item'Class) return Boolean is
|
|
(Self.Is_Ordered);
|
|
|
|
------------
|
|
-- Marker --
|
|
------------
|
|
|
|
function Marker (Self : List_Item'Class)
|
|
return VSS.Strings.Virtual_String is (Self.Marker);
|
|
|
|
function Marker (Self : List_Item'Class) return Natural
|
|
is (Self.Marker_Value);
|
|
|
|
------------------
|
|
-- Marker_Value --
|
|
------------------
|
|
|
|
function Marker_Value
|
|
(Match : VSS.Regular_Expressions.Regular_Expression_Match)
|
|
return Natural is
|
|
begin
|
|
-- GCC 12 generates wrong code if condition expression is used here
|
|
if Match.Marker (3).Is_Valid then
|
|
return Natural'Wide_Wide_Value
|
|
(VSS.Strings.Conversions.To_Wide_Wide_String
|
|
(Match.Captured (3)));
|
|
else
|
|
return 0;
|
|
end if;
|
|
end Marker_Value;
|
|
|
|
end Markdown.Implementation.List_Items;
|