-- -- Copyright (C) 2014-2022, AdaCore -- SPDX-License-Identifier: Apache-2.0 -- -- .. note:: At this stage, this package is considered as internal. -- -- This package provides a generic API so that programs can unparse trees for -- any Langkit-generated library, i.e. turn parse trees back to text sources, -- with custom formatting. Formatting features are based on the Prettier_Ada -- library. -- -- Here is a simplistic pretty-printing program using this API: -- -- .. code-block:: ada -- -- with Ada.Command_Line; -- with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; -- with Ada.Text_IO; use Ada.Text_IO; -- with Ada.Text_IO.Unbounded_IO; use Ada.Text_IO.Unbounded_IO; -- -- with Langkit_Support.Diagnostics; use Langkit_Support.Diagnostics; -- with Langkit_Support.Generic_API; use Langkit_Support.Generic_API; -- with Langkit_Support.Generic_API.Analysis; -- use Langkit_Support.Generic_API.Analysis; -- with Langkit_Support.Generic_API.Unparsing; -- use Langkit_Support.Generic_API.Unparsing; -- -- with Libfoolang.Generic_API; use Libfoolang.Generic_API; -- -- with Prettier_Ada.Documents; -- use Prettier_Ada.Documents; -- -- procedure Unparse is -- -- -- Load the unparsing configuration file, that determines how to -- -- format source code for the "foo" language: see the -- -- Load_Unparsing_Config function for more information about this -- -- file. -- -- Diagnostics : Diagnostics_Vectors.Vector; -- Config : constant Unparsing_Configuration := -- Load_Unparsing_Config (Self_Id, "config.json", Diagnostics); -- -- -- Parse the source file to reformat -- -- Ctx : constant Lk_Context := -- Create_Context (Libfoolang.Generic_API.Self_Id); -- U : constant Lk_Unit := -- Ctx.Get_From_File (Ada.Command_Line.Argument (1)); -- begin -- -- If we were unable to load the unparsing configuration, bail out -- -- if Config = No_Unparsing_Configuration then -- Put_Line ("Error when loading the unparsing configuration:"); -- Print (Diagnostics); -- raise Program_Error; -- end if; -- -- -- If the source file to reformat has parsing errors, bail out -- -- if U.Has_Diagnostics then -- Put_Line ("Parsing errors:"); -- for D of U.Diagnostics loop -- Put_Line (U.Format_GNU_Diagnostic (D)); -- end loop; -- raise Program_Error; -- end if; -- -- -- Otherwise, print the reformatted source to the standard output -- -- declare -- Doc : constant Document_Type := -- Unparse_To_Prettier (U.Root, Config); -- Formatted : constant Unbounded_String := -- Format (Doc, Default_Format_Options); -- begin -- Put_Line (Formatted); -- end; -- end Unparse; private with Ada.Finalization; with GNATCOLL.Traces; with Prettier_Ada.Documents; with Langkit_Support.Diagnostics; use Langkit_Support.Diagnostics; with Langkit_Support.Generic_API.Analysis; use Langkit_Support.Generic_API.Analysis; package Langkit_Support.Generic_API.Unparsing is type Unparsing_Configuration is private; -- Configuration that customizes how source fragments are turned into a -- prettier document. No_Unparsing_Configuration : constant Unparsing_Configuration; -- Special value to mean the absence of an unparsing configuration function Default_Unparsing_Configuration (Language : Language_Id) return Unparsing_Configuration; -- Return the default unparsing configuration for the given language function Load_Unparsing_Config (Language : Language_Id; Filename : String; Diagnostics : in out Diagnostics_Vectors.Vector) return Unparsing_Configuration; -- Read and parse the unparsing configuration for the given Language from -- Filename. Append error messages to ``Diagnostics`` and return -- ``No_Unparsing_Configuration`` if an error occurs while reading the -- configuration file. -- -- The configuration is a JSON file that provides "document templates": -- patterns to generate Prettier documents: -- -- * The default template to unparse nodes or node fields is the -- "recurse" one. Instantiating it just yields the default unparsing -- for that node/field:: -- -- "recurse" -- -- * The "breakParent" template yields a "breakParent" Prettier -- Document:: -- -- "breakParent" -- -- * The "line"/"hardline"/"hardLineWithoutBreakPararent"/"softline"/ -- "literalline" templates yield the corresponding Prettier documents:: -- -- "line" -- "hardline" -- "hardlineWithoutBreakParent" -- "softline" -- "literalline" -- -- * The "flushLineBreaks" template is used as a placeholder to emit -- potential line breaks that come from the source code to reformat:: -- -- "flushLineBreaks" -- -- * The "trim" template yields a "trim" Prettier Document:: -- -- "trim" -- -- * The "whitespace" template yields a "text" document with the -- specified amount of spaces:: -- -- {"kind": "whitespace", "length": 2} -- -- /* or just, for length = 1 */ -- -- "whitespace" -- -- * The "align" template yields a "align" Prettier document:: -- -- { -- "kind": "align", -- "width": , -- "contents": -- } -- -- * The "dedent" template yields a "dedent" Prettier document:: -- -- {"kind": "dedent", "document": } -- -- * The "dedentToRoot" template yields a "dedentToRoot" Prettier -- document:: -- -- {"kind": "dedentToRoot", "document": } -- -- * The "fill" template yields a "fill" Prettier document:: -- -- {"kind": "fill", "document": } -- -- * The "group" template yields a "group" Prettier document:: -- -- {"kind": "group", "document": } -- {"kind": "group", "document": , "shouldBreak": true} -- -- An optional "id" field makes it define a symbol to reference in the -- same template: -- -- {"kind": "group", "document": , "id": "mySymbol"} -- -- * The "ifBreak" template yields an "ifBreak" Prettier document:: -- -- {"kind": "ifBreak", "breakContents": } -- { -- "kind": "ifBreak", -- "breakContents": , -- "flatContents": -- } -- { -- "kind": "ifBreak", -- "breakContents": , -- "flatContents": , -- "groupId": -- } -- -- * The "ifEmpty" template is valid only inside a fields configuration. -- It yields its "then" alternative if the field is an empty list, and -- its "else" alternative otherwise:: -- -- { -- "kind": "ifEmpty", -- "then": , -- "else": -- } -- -- * The "ifKind" template is valid only inside a node configuration. -- The "null" entry is optional. If "field" is a null node, "null" -- is yielded if defined, otherwise fallsback to "default". If "field" -- is not a null node, it yields the match from its "matchers" field -- whose key matches with the kind of its "field" key, or "default" if -- none matches:: -- -- { -- "kind": "ifKind", -- "field": "", -- "matchers": [ -- {"kind": , "document": }, -- ... -- ], -- "default": -- "null": -- } -- -- A variant is available in field templates. It has no "field" entry: -- the alternative is picked depending on the field that owns this -- template. -- -- * The "indent" template yields an "indent" Prettier document:: -- -- {"kind": "indent", "contents": } -- -- * The "markAsRoot" template yields a "markAsRoot" Prettier document:: -- -- {"kind": "markAsRoot", "contents": } -- -- * The "innerRoot" template yields a "innerRoot" Prettier document:: -- -- {"kind": "innerRoot", "contents": } -- -- * The "continuationLineIndent" template yields a -- "continuationLineIndent" Prettier document:: -- -- {"kind": "continuationLineIndent", "contents": } -- -- * The "recurse_field" template is valid only in "node" templates for -- concrete nodes that are neither abstract, token nor list nodes. When -- used, the whole template cannot contain any -- "recurse"/"recurse_flatten" template, and the template, once -- linearized, must reflect how the node is unparsed. -- -- For example, let's consider that the ``VarDecl`` node is created -- parsing the following chunks:: -- -- "var" [f_name] ":" [f_type] ";" -- -- Then its "node" template must contain two "recurse_field" templates -- for the two fields, in the same order, and with the same tokens in -- between. For instance:: -- -- [ -- {"kind": "text", "text": "var"}, -- {"kind": "recurse_field", "text": "f_name"}, -- { -- "kind": "group", -- "document": [ -- {"kind": "text", "text": ":"}, -- {"kind": "recurse_field", "text": "f_type"} -- ] -- }, -- {"kind": "text", "text": ";"}, -- ] -- -- * The "recurse_flatten" template acts like "recurse" but refines its -- result so that the document nested in "align", "fill", "group", -- "indent" templates and in 1-item document lists is returned -- instead (recursively):: -- -- {"kind": "recurse_flatten", "if": ["Node1" ,"Node2", ...]} -- -- The "if" entry is optional. If provided, it must contain a list of -- node type names; in this case the flattening is applied only for -- templates that were instantiated for nodes that match at least one -- of the node types. -- -- * The "tableSeparator" template yields the corresponding Prettier -- document:: -- -- {"kind": "tableSeparator", "text": "some_text_to_unparse"} -- -- * The "text" template yields a "text" Prettier document:: -- -- {"kind": "text", "text": "some_text_to_unparse"} -- -- Using this template is valid in specific contexts only: -- -- * For "node" templates, when used with "recurse_field" template: see -- the documentation for "recurse_field"; -- -- * For "fields" templates: in this case, the linearized template must -- reflect how the field is unparsed. See the documentation for -- "recurse_field" to have more information about linearization. -- -- * A JSON list yields the corresponding "list" Prettier document:: -- -- [{"kind": "whitespace"}, {"kind": "recurse"}] -- -- The configuration file has the following format:: -- -- { -- "node_configs": {: }, -- "max_empty_lines": -- } -- -- For each node to configure, the inner "node_configs" mapping associates -- the name of the node (as a string key) to another mapping with the -- following format:: -- -- { -- "node":