------------------------------------------------------------------------------
--                         Language Server Protocol                         --
--                                                                          --
--                     Copyright (C) 2018-2023, AdaCore                     --
--                                                                          --
-- This is free software;  you can redistribute it  and/or modify it  under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  This software is distributed in the hope  that it will be useful, --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
-- License for  more details.  You should have  received  a copy of the GNU --
-- General  Public  License  distributed  with  this  software;   see  file --
-- COPYING3.  If not, go to http://www.gnu.org/licenses for a complete copy --
-- of the license.                                                          --
------------------------------------------------------------------------------

with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Wide_Wide_Unbounded.Wide_Wide_Hash;
with Ada.Strings.Wide_Wide_Fixed.Wide_Wide_Hash;
with Interfaces;

with VSS.JSON.Pull_Readers;
with VSS.JSON.Streams;
with VSS.Strings.Conversions;

with LSP.JSON_Streams;

with URIs;

package body LSP.Types is

   use type VSS.JSON.Streams.JSON_Stream_Element_Kind;

   function No_Any return LSP_Any is
     (GNATCOLL.JSON.JSON_Null with null record);

   function Empty return LSP_Any is
     (GNATCOLL.JSON.Create_Object with null record);

   --------------
   -- Assigned --
   --------------

   function Assigned (Id : LSP_Number_Or_String) return Boolean is
   begin
      return Id.Is_Number or else not Id.String.Is_Empty;
   end Assigned;

   -----------
   -- Equal --
   -----------

   function Equal (Left, Right : LSP_URI) return Boolean is
      use type VSS.Strings.Virtual_String;

   begin
      return Left.URI = Right.URI;
   end Equal;

   -----------------
   -- File_To_URI --
   -----------------

   function File_To_URI (File : String) return LSP.Types.LSP_URI is
      Result : constant URIs.URI_String :=
        URIs.Conversions.From_File (File);
   begin
      return (URI => VSS.Strings.Conversions.To_Virtual_String (Result));
   end File_To_URI;

   function File_To_URI (File : Ada.Strings.Unbounded.Unbounded_String)
     return LSP.Types.LSP_URI is
   begin
      return File_To_URI (Ada.Strings.Unbounded.To_String (File));
   end File_To_URI;

   ----------
   -- Hash --
   ----------

   function Hash
     (Item : VSS.Strings.Virtual_String) return Ada.Containers.Hash_Type is
   begin
      return
        Ada.Strings.Wide_Wide_Unbounded.Wide_Wide_Hash
          (VSS.Strings.Conversions.To_Unbounded_Wide_Wide_String (Item));
   end Hash;

   ----------
   -- Hash --
   ----------

   function Hash
     (Item : LSP.Types.LSP_Number_Or_String)
      return Ada.Containers.Hash_Type is
   begin
      if Item.Is_Number then
         return Ada.Containers.Hash_Type'Val (Item.Number);

      else
         return LSP.Types.Hash (Item.String);
      end if;
   end Hash;

   ----------
   -- Hash --
   ----------

   function Hash (Item : LSP_URI) return Ada.Containers.Hash_Type is
     (Ada.Strings.Wide_Wide_Fixed.Wide_Wide_Hash
        (VSS.Strings.Conversions.To_Wide_Wide_String (Item.URI)));

   ------------------
   -- Read_LSP_URI --
   ------------------

   procedure Read_LSP_URI
     (S    : access Ada.Streams.Root_Stream_Type'Class;
      Item : out LSP_URI) is
   begin
      Read_String (S, Item.URI);
   end Read_LSP_URI;

   -----------------
   -- Read_String --
   -----------------

   procedure Read_String
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : out VSS.Strings.Virtual_String)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);

   begin
      pragma Assert (JS.R.Is_String_Value);
      V := JS.R.String_Value;
      JS.R.Read_Next;
   end Read_String;

   --------------
   -- Read_Any --
   --------------

   procedure Read_Any
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : out LSP_Any)
   is
      function Read_Value return GNATCOLL.JSON.JSON_Value;

      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);

      ----------------
      -- Read_Value --
      ----------------

      function Read_Value return GNATCOLL.JSON.JSON_Value is
      begin
         case JS.R.Element_Kind is
            when VSS.JSON.Streams.None |
                 VSS.JSON.Streams.Invalid |
                 VSS.JSON.Streams.Start_Document |
                 VSS.JSON.Streams.End_Document |
                 VSS.JSON.Streams.Comment |
                 VSS.JSON.Streams.End_Array |
                 VSS.JSON.Streams.End_Object |
                 VSS.JSON.Streams.Key_Name =>

               raise Program_Error;

            when VSS.JSON.Streams.Start_Array =>

               return Result : constant GNATCOLL.JSON.JSON_Value :=
                 GNATCOLL.JSON.Create (GNATCOLL.JSON.Empty_Array)
               do
                  JS.R.Read_Next;

                  while not JS.R.Is_End_Array loop
                     GNATCOLL.JSON.Append (Result, Read_Value);
                  end loop;

                  JS.R.Read_Next;
               end return;

            when VSS.JSON.Streams.Start_Object =>

               return Result : constant GNATCOLL.JSON.JSON_Value :=
                 GNATCOLL.JSON.Create_Object
               do
                  JS.R.Read_Next;

                  while not JS.R.Is_End_Object loop
                     pragma Assert (JS.R.Is_Key_Name);

                     declare
                        Key : constant String :=
                          VSS.Strings.Conversions.To_UTF_8_String
                            (JS.R.Key_Name);
                     begin
                        JS.R.Read_Next;
                        Result.Set_Field (Key, Read_Value);
                     end;
                  end loop;

                  JS.R.Read_Next;
               end return;

            when VSS.JSON.Streams.String_Value =>

               declare
                  Value : constant String :=
                    VSS.Strings.Conversions.To_UTF_8_String
                      (JS.R.String_Value);
               begin
                  JS.R.Read_Next;

                  return GNATCOLL.JSON.Create (Value);
               end;

            when VSS.JSON.Streams.Number_Value =>

               declare
                  Value : constant VSS.JSON.JSON_Number :=
                    JS.R.Number_Value;
               begin
                  JS.R.Read_Next;

                  case Value.Kind is
                     when VSS.JSON.JSON_Integer =>
                        return GNATCOLL.JSON.Create
                          (Long_Long_Integer (Value.Integer_Value));
                     when VSS.JSON.JSON_Float =>
                        return GNATCOLL.JSON.Create
                          (Long_Float (Value.Float_Value));
                     when others =>
                        raise Program_Error;
                  end case;
               end;

            when VSS.JSON.Streams.Boolean_Value =>

               declare
                  Value : constant Boolean := (JS.R.Boolean_Value);
               begin
                  JS.R.Read_Next;

                  return GNATCOLL.JSON.Create (Value);
               end;

            when VSS.JSON.Streams.Null_Value =>
               JS.R.Read_Next;

               return GNATCOLL.JSON.JSON_Null;

         end case;
      end Read_Value;
   begin
      case JS.R.Element_Kind is
         when VSS.JSON.Streams.None |
              VSS.JSON.Streams.Invalid |
              VSS.JSON.Streams.Start_Document |
              VSS.JSON.Streams.End_Document |
              VSS.JSON.Streams.Comment |
              VSS.JSON.Streams.End_Array |
              VSS.JSON.Streams.End_Object |
              VSS.JSON.Streams.Key_Name =>

            raise Program_Error;

         when VSS.JSON.Streams.Start_Array |
              VSS.JSON.Streams.Start_Object |
              VSS.JSON.Streams.String_Value |
              VSS.JSON.Streams.Number_Value |
              VSS.JSON.Streams.Boolean_Value |
              VSS.JSON.Streams.Null_Value =>

            V := (Read_Value with null record);
      end case;
   end Read_Any;

   ------------------
   -- Read_Boolean --
   ------------------

   procedure Read_Boolean
    (Stream : in out LSP.JSON_Streams.JSON_Stream'Class;
     Item   : out Boolean) is
   begin
      case Stream.R.Element_Kind is
         when VSS.JSON.Streams.Boolean_Value =>

            Item := Stream.R.Boolean_Value;
            Stream.R.Read_Next;

         when VSS.JSON.Streams.Null_Value =>

            Item := False;
            Stream.R.Read_Next;

         when others =>
            Item := True;
            Stream.Skip_Value;
            --  Property of non-boolean type, protocol extension
            --  could provide an object instead of boolean.
      end case;

   end Read_Boolean;

   --------------------------------
   -- Read_LSP_Boolean_Or_String --
   --------------------------------

   procedure Read_LSP_Boolean_Or_String
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : out LSP_Boolean_Or_String)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);
   begin
      case JS.R.Element_Kind is
         when VSS.JSON.Streams.String_Value =>
            V := (Is_Boolean => False,
                  String     => JS.R.String_Value);
            JS.R.Read_Next;

         when VSS.JSON.Streams.Boolean_Value =>
            V := (Is_Boolean => True,
                  Boolean    => JS.R.Boolean_Value);
            JS.R.Read_Next;

         when others =>
            V := (Is_Boolean => True,
                  Boolean    => True);

            JS.Skip_Value;
      end case;

   end Read_LSP_Boolean_Or_String;

   -------------------------------
   -- Read_LSP_Number_Or_String --
   -------------------------------

   procedure Read_LSP_Number_Or_String
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : out LSP_Number_Or_String)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);
   begin
      case JS.R.Element_Kind is
         when VSS.JSON.Streams.Null_Value =>

            V := (Is_Number => False,
                  String    => <>);

         when VSS.JSON.Streams.String_Value =>
            V := (Is_Number => False,
                  String    => JS.R.String_Value);

         when VSS.JSON.Streams.Number_Value =>
            V := (Is_Number => True,
                  Number    => LSP_Number (JS.R.Number_Value.Integer_Value));

         when others =>
            raise Constraint_Error;
      end case;

      JS.R.Read_Next;
   end Read_LSP_Number_Or_String;

   ---------------------------
   -- Read_Optional_Boolean --
   ---------------------------

   procedure Read_Optional_Boolean
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : out Optional_Boolean)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);

   begin
      case JS.R.Element_Kind is
         when VSS.JSON.Streams.Null_Value =>
            V := (Is_Set => False);
            JS.R.Read_Next;

         when VSS.JSON.Streams.Boolean_Value =>
            V := (Is_Set => True,
                  Value  => JS.R.Boolean_Value);
            JS.R.Read_Next;

         when others =>
            V := (Is_Set => True,
                  Value  => True);
            JS.Skip_Value;
      end case;

   end Read_Optional_Boolean;

   ----------
   -- Read --
   ----------

   procedure Read
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : out LSP_Number)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);

   begin
      pragma Assert (JS.R.Is_Number_Value);
      V := LSP_Number (JS.R.Number_Value.Integer_Value);
      JS.R.Read_Next;
   end Read;

   --------------------------
   -- Read_Nullable_String --
   --------------------------

   procedure Read_Nullable_String
     (S    : access Ada.Streams.Root_Stream_Type'Class;
      Item : out Nullable_String)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);

   begin
      case JS.R.Element_Kind is
         when VSS.JSON.Streams.Null_Value =>

            Item := (Is_Set => False);

         when VSS.JSON.Streams.String_Value =>
            Item := (Is_Set => True,
                     Value  => JS.R.String_Value);

         when others =>
            raise Constraint_Error;
      end case;

      JS.R.Read_Next;
   end Read_Nullable_String;

   ------------------------
   -- Read_String_Vector --
   ------------------------

   procedure Read_String_Vector
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : out VSS.String_Vectors.Virtual_String_Vector)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);

   begin
      V.Clear;

      if JS.R.Is_Start_Array then
         JS.R.Read_Next;

         while not JS.R.Is_End_Array loop
            declare
               Item : VSS.Strings.Virtual_String;

            begin
               LSP.Types.Read_String (S, Item);
               V.Append (Item);
            end;
         end loop;

      elsif JS.R.Is_String_Value then
         V.Append (JS.R.String_Value);
      end if;

      JS.R.Read_Next;
   end Read_String_Vector;

   --------------------------------
   -- Read_UTF16_Code_Unit_Count --
   --------------------------------

   procedure Read_UTF16_Code_Unit_Count
    (Stream : in out LSP.JSON_Streams.JSON_Stream'Class;
     Item   : out VSS.Unicode.UTF16_Code_Unit_Count) is
   begin
      pragma Assert (Stream.R.Is_Number_Value);
      Item :=
        VSS.Unicode.UTF16_Code_Unit_Count
          (Stream.R.Number_Value.Integer_Value);
      Stream.R.Read_Next;
   end Read_UTF16_Code_Unit_Count;

   ----------------
   -- To_LSP_URI --
   ----------------

   function To_LSP_URI (Item : VSS.Strings.Virtual_String) return LSP_URI is
   begin
      return (URI => Item);
   end To_LSP_URI;

   -----------------------
   -- To_Virtual_String --
   -----------------------

   function To_Virtual_String (Item : LSP.Types.LSP_Number_Or_String)
      return VSS.Strings.Virtual_String is
   begin
      if Item.Is_Number then
         declare
            Image : constant Wide_Wide_String :=
              LSP_Number'Wide_Wide_Image (Item.Number);
         begin
            return
              VSS.Strings.To_Virtual_String
                (Image (Image'First + 1 .. Image'Last));
         end;
      else
         return Item.String;
      end if;
   end To_Virtual_String;

   -----------------------
   -- To_Virtual_String --
   -----------------------

   function To_Virtual_String
     (Self : LSP_URI) return VSS.Strings.Virtual_String is
   begin
      return Self.URI;
   end To_Virtual_String;

   ---------------------
   -- To_UTF_8_String --
   ---------------------

   function To_UTF_8_String
     (Item : LSP_URI) return Ada.Strings.UTF_Encoding.UTF_8_String is
   begin
      return VSS.Strings.Conversions.To_UTF_8_String (Item.URI);
   end To_UTF_8_String;

   ---------------
   -- Write_Any --
   ---------------

   procedure Write_Any
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : LSP_Any)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);

      procedure Write (Value : GNATCOLL.JSON.JSON_Value'Class);
      procedure Write_Field (Key : String; Value : GNATCOLL.JSON.JSON_Value);

      -----------
      -- Write --
      -----------

      procedure Write (Value : GNATCOLL.JSON.JSON_Value'Class) is
      begin
         case Value.Kind is
            when GNATCOLL.JSON.JSON_Null_Type =>
               JS.Write_Null;
            when GNATCOLL.JSON.JSON_Boolean_Type =>
               JS.Write_Boolean (Value.Get);
            when GNATCOLL.JSON.JSON_Int_Type =>
               JS.Write_Integer
                 (Interfaces.Integer_64 (Long_Long_Integer'(Value.Get)));
            when GNATCOLL.JSON.JSON_Float_Type =>
               JS.Write_Integer
                 (Interfaces.Integer_64 (Float'(Value.Get)));
            when GNATCOLL.JSON.JSON_String_Type =>
               JS.Write_String
                 (VSS.Strings.Conversions.To_Virtual_String
                    (Ada.Strings.Unbounded.Unbounded_String'(Value.Get)));
            when GNATCOLL.JSON.JSON_Array_Type =>
               declare
                  Vector : constant GNATCOLL.JSON.JSON_Array := Value.Get;
               begin
                  JS.Start_Array;
                  for J in 1 .. GNATCOLL.JSON.Length (Vector) loop
                     Write (GNATCOLL.JSON.Get (Vector, J));
                  end loop;
                  JS.End_Array;
               end;
            when GNATCOLL.JSON.JSON_Object_Type =>
               JS.Start_Object;
               Value.Map_JSON_Object (Write_Field'Access);
               JS.End_Object;
         end case;
      end Write;

      -----------------
      -- Write_Field --
      -----------------

      procedure Write_Field (Key : String; Value : GNATCOLL.JSON.JSON_Value) is
      begin
         JS.Key (VSS.Strings.Conversions.To_Virtual_String (Key));
         Write (Value);
      end Write_Field;
   begin
      Write (V);
   end Write_Any;

   -------------------
   -- Write_Boolean --
   -------------------

   procedure Write_Boolean
    (Stream : in out LSP.JSON_Streams.JSON_Stream'Class;
     Key    : VSS.Strings.Virtual_String;
     Item   : Boolean) is
   begin
      Stream.Key (Key);
      Stream.Write_Boolean (Item);
   end Write_Boolean;

   ------------------
   -- Write_Number --
   ------------------

   procedure Write_Number
    (Stream : in out LSP.JSON_Streams.JSON_Stream'Class;
     Key    : VSS.Strings.Virtual_String;
     Item   : LSP.Types.LSP_Number) is
   begin
      Stream.Key (Key);
      Stream.Write_Integer (Interfaces.Integer_64 (Item));
   end Write_Number;

   ----------------------------
   -- Write_Optional_Boolean --
   ----------------------------

   procedure Write_Optional_Boolean
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : Optional_Boolean)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);
   begin
      if V.Is_Set then
         JS.Write_Boolean (V.Value);
      end if;
   end Write_Optional_Boolean;

   ----------------------------
   -- Write_Optional_Boolean --
   ----------------------------

   procedure Write_Optional_Boolean
    (Stream : in out LSP.JSON_Streams.JSON_Stream'Class;
     Key    : VSS.Strings.Virtual_String;
     Item   : LSP.Types.Optional_Boolean) is
   begin
      if Item.Is_Set then
         Stream.Key (Key);
         Stream.Write_Boolean (Item.Value);
      end if;
   end Write_Optional_Boolean;

   ---------------------------
   -- Write_Nullable_String --
   ---------------------------

   procedure Write_Nullable_String
     (S    : access Ada.Streams.Root_Stream_Type'Class;
      Item : Nullable_String)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);
   begin
      if Item.Is_Set then
         JS.Write_String (Item.Value);
      else
         JS.Write_Null;
      end if;
   end Write_Nullable_String;

   -----------
   -- Write --
   -----------

   procedure Write
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : LSP_Number)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);
   begin
      JS.Write_Integer (Interfaces.Integer_64 (V));
   end Write;

   ------------------
   -- Write_String --
   ------------------

   procedure Write_String
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : VSS.Strings.Virtual_String)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);
   begin
      JS.Write_String (V);
   end Write_String;

   ------------------
   -- Write_String --
   ------------------

   procedure Write_String
    (Stream : in out LSP.JSON_Streams.JSON_Stream'Class;
     Key    : VSS.Strings.Virtual_String;
     Item   : VSS.Strings.Virtual_String) is
   begin
      Stream.Key (Key);
      Stream.Write_String (Item);
   end Write_String;

   -------------------
   -- Write_LSP_URI --
   -------------------

   procedure Write_LSP_URI
     (S    : access Ada.Streams.Root_Stream_Type'Class;
      Item : LSP_URI) is
   begin
      Write_String (S, Item.URI);
   end Write_LSP_URI;

   --------------------------------
   -- Write_LSP_Number_Or_String --
   --------------------------------

   procedure Write_LSP_Number_Or_String
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : LSP_Number_Or_String) is
   begin
      if V.Is_Number then
         Write (S, V.Number);
      elsif not V.String.Is_Empty then
         Write_String (S, V.String);
      end if;
   end Write_LSP_Number_Or_String;

   ----------------------------
   -- Write_Number_Or_String --
   ----------------------------

   procedure Write_Number_Or_String
    (Stream : in out LSP.JSON_Streams.JSON_Stream'Class;
     Key    : VSS.Strings.Virtual_String;
     Item   : LSP.Types.LSP_Number_Or_String) is
   begin
      if Item.Is_Number then
         Write_Number (Stream, Key, Item.Number);
      elsif not Item.String.Is_Empty then
         Write_String (Stream, Key, Item.String);
      end if;
   end Write_Number_Or_String;

   ---------------------------------
   -- Write_LSP_Boolean_Or_String --
   ---------------------------------

   procedure Write_LSP_Boolean_Or_String
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : LSP_Boolean_Or_String)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);
   begin
      case V.Is_Boolean is
         when True =>
            JS.Write_Boolean (V.Boolean);
         when False =>
            JS.Write_String (V.String);
      end case;
   end Write_LSP_Boolean_Or_String;

   -------------------------
   -- Write_String_Vector --
   -------------------------

   procedure Write_String_Vector
     (S : access Ada.Streams.Root_Stream_Type'Class;
      V : VSS.String_Vectors.Virtual_String_Vector)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);

   begin
      JS.Start_Array;

      for J in 1 .. V.Length loop
         JS.Write_String (V.Element (J));
      end loop;

      JS.End_Array;
   end Write_String_Vector;

   ---------------------------------
   -- Write_UTF16_Code_Unit_Count --
   ---------------------------------

   procedure Write_UTF16_Code_Unit_Count
    (Stream : in out LSP.JSON_Streams.JSON_Stream'Class;
     Item   : VSS.Unicode.UTF16_Code_Unit_Count) is
   begin
      Stream.Write_Integer (Interfaces.Integer_64 (Item));
   end Write_UTF16_Code_Unit_Count;

   ----------------------------------
   -- Read_Optional_Virtual_String --
   ----------------------------------

   procedure Read_Optional_Virtual_String
     (S    : access Ada.Streams.Root_Stream_Type'Class;
      Item : out Optional_Virtual_String)
   is
      JS : LSP.JSON_Streams.JSON_Stream'Class renames
        LSP.JSON_Streams.JSON_Stream'Class (S.all);

   begin
      case JS.R.Element_Kind is
         when VSS.JSON.Streams.Null_Value =>

            Item := (Is_Set => False);

         when VSS.JSON.Streams.String_Value =>
            Item := (Is_Set => True,
                     Value  => JS.R.String_Value);

         when others =>
            raise Constraint_Error;
      end case;

      JS.R.Read_Next;
   end Read_Optional_Virtual_String;

   -----------------------------------
   -- Write_Optional_Virtual_String --
   -----------------------------------

   procedure Write_Optional_Virtual_String
     (S    : access Ada.Streams.Root_Stream_Type'Class;
      Item : Optional_Virtual_String) is
   begin
      if Item.Is_Set then
         Write_String (S, Item.Value);
      end if;
   end Write_Optional_Virtual_String;

end LSP.Types;
