-- -- Copyright (C) 2021-2023, AdaCore -- -- SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -- -- Simple parser for code spans with VSS.Characters.Latin; with VSS.Regular_Expressions; with VSS.Strings.Character_Iterators; with VSS.Strings.Cursors.Markers; with Markdown.Inlines; package body Markdown.Implementation.Code_Spans is Open_Pattern : constant Wide_Wide_String := "(?:[^`\\]|\\[^\f])*(`+)"; -- 1 <- group index Close_Pattern : constant Wide_Wide_String := "`+"; Open : VSS.Regular_Expressions.Regular_Expression; -- Regexp of Open_Pattern Close : VSS.Regular_Expressions.Regular_Expression; -- Regexp of Close_Pattern procedure Find_Code_Span (Text : VSS.Strings.Virtual_String; From : VSS.Strings.Cursors.Markers.Character_Marker; Length : VSS.Strings.Character_Count; Span : in out Markdown.Simple_Inline_Parsers.Inline_Span); -------------------- -- Find_Code_Span -- -------------------- procedure Find_Code_Span (Text : VSS.Strings.Virtual_String; From : VSS.Strings.Cursors.Markers.Character_Marker; Length : VSS.Strings.Character_Count; Span : in out Markdown.Simple_Inline_Parsers.Inline_Span) is Match : VSS.Regular_Expressions.Regular_Expression_Match; Pos : VSS.Strings.Character_Iterators.Character_Iterator; begin Pos.Set_At (From); Forward (Pos, Length); -- skip open backticks while Pos.Has_Element loop Match := Close.Match (Text, Pos); exit when not Match.Has_Match; if Match.Captured.Character_Length = Length then declare Plain : VSS.Strings.Virtual_String; Start : VSS.Strings.Character_Iterators.Character_Iterator; Stop : VSS.Strings.Character_Iterators.Character_Iterator; Inline : Markdown.Inlines.Inline; Ignore : Boolean; begin Start.Set_At (From); Forward (Start, Length); -- skip open backticks Stop.Set_At (Match.First_Marker); Ignore := Stop.Backward; if Start.Element in ' ' | VSS.Characters.Latin.Line_Feed and Stop.Element in ' ' | VSS.Characters.Latin.Line_Feed then Forward (Start); Ignore := Stop.Backward; end if; Plain := Text.Slice (Start, Stop); Inline := (Kind => Markdown.Inlines.Code_Span, Code_Span => Plain); Span := (Is_Set => True, From => From, To => Match.Last_Marker, Annotation => Inline, Text => <>); return; end; end if; -- Continue search after match Pos.Set_At (Match.Last_Marker); Forward (Pos); end loop; end Find_Code_Span; ---------------- -- Initialize -- ---------------- procedure Initialize is begin if not Open.Is_Valid then -- Construct Open/Close regexps Open := VSS.Regular_Expressions.To_Regular_Expression (VSS.Strings.To_Virtual_String (Open_Pattern)); Close := VSS.Regular_Expressions.To_Regular_Expression (VSS.Strings.To_Virtual_String (Close_Pattern)); end if; end Initialize; --------------------- -- Parse_Code_Span -- --------------------- procedure Parse_Code_Span (Text : VSS.Strings.Virtual_String; From : VSS.Strings.Cursors.Abstract_Character_Cursor'Class; Span : out Markdown.Simple_Inline_Parsers.Inline_Span) is Match : VSS.Regular_Expressions.Regular_Expression_Match; Pos : VSS.Strings.Character_Iterators.Character_Iterator; begin Initialize; Span := (Is_Set => False); Pos.Set_At (From); while Pos.Has_Element and not Span.Is_Set loop Match := Open.Match (Text, Pos); exit when not Match.Has_Match; Find_Code_Span (Text, Match.First_Marker (1), Match.Captured (1).Character_Length, Span); -- Step after match Pos.Set_At (Match.Last_Marker); Forward (Pos); end loop; end Parse_Code_Span; end Markdown.Implementation.Code_Spans;