mirror of
https://github.com/AdaCore/langkit.git
synced 2026-02-12 12:28:12 -08:00
Q322-037: Implement proper snapping
Users can annotate node that they want to have snapping sloc behavior. Additionally, incomplete nodes snap at the end. Change-Id: Ib3b8d3337d1356059b01694b547265386afd814f
This commit is contained in:
@@ -2303,6 +2303,34 @@ class ASTNodeType(BaseStructType):
|
||||
)),
|
||||
]
|
||||
|
||||
def snaps(self, anchor_end):
|
||||
"""
|
||||
Whether this node type snaps. To see what this means, see Annotations
|
||||
documentation.
|
||||
"""
|
||||
from langkit.parsers import _Transform
|
||||
|
||||
if not self.parser:
|
||||
return False
|
||||
|
||||
i = -1 if anchor_end else 0
|
||||
|
||||
return self.annotations.snaps or (
|
||||
isinstance(self.parser, _Transform)
|
||||
and self.parser.parser.parsers
|
||||
and self.parser.parser.parsers[i].get_type()
|
||||
and self.parser.parser.parsers[i].get_type().is_ast_node
|
||||
and self.parser.parser.parsers[i].get_type().snaps(anchor_end)
|
||||
)
|
||||
|
||||
@property
|
||||
def snaps_at_start(self):
|
||||
return self.snaps(False)
|
||||
|
||||
@property
|
||||
def snaps_at_end(self):
|
||||
return self.snaps(True)
|
||||
|
||||
|
||||
# We tag the ASTNodeType class as abstract here, because of the circular
|
||||
# dependency between the @abstract decorator and the ASTNodeType class, which
|
||||
|
||||
@@ -302,7 +302,7 @@ inherited_annotation = inherited_property(lambda s: s.get_parent_annotations())
|
||||
class Annotations(object):
|
||||
def __init__(self, repr_name=None, generic_list_type=None,
|
||||
warn_on_node=None, rebindable=False,
|
||||
custom_short_image=False):
|
||||
custom_short_image=False, snaps=False):
|
||||
"""
|
||||
Constructor for a node's annotations.
|
||||
|
||||
@@ -318,12 +318,17 @@ class Annotations(object):
|
||||
declaration and the definition of a function called
|
||||
`[NODE_NAME]_Short_Image` that takes the node in argument and that
|
||||
returns a `Text_Type` value.
|
||||
:param bool snaps: Whether this node's SLOCs are supposed to snap or
|
||||
not. Snapping designates the behavior where the start SLOC will be
|
||||
anchored to the previous token's end SLOC rather than the node's
|
||||
first token start SLOC, and conversely for the end SLOC.
|
||||
"""
|
||||
self.repr_name = repr_name
|
||||
self.generic_list_type = generic_list_type
|
||||
self._warn_on_node = warn_on_node
|
||||
self._rebindable = rebindable
|
||||
self.custom_short_image = custom_short_image
|
||||
self._snaps = snaps
|
||||
|
||||
@inherited_annotation
|
||||
def warn_on_node(self):
|
||||
@@ -364,6 +369,10 @@ class Annotations(object):
|
||||
bn = self.node.base
|
||||
return bn.annotations if bn else None
|
||||
|
||||
@inherited_annotation
|
||||
def snaps(self):
|
||||
return self._snaps
|
||||
|
||||
|
||||
class _ASTNodeMetaclass(type):
|
||||
"""
|
||||
|
||||
@@ -101,6 +101,12 @@ package body ${ada_lib_name}.Analysis.Implementation is
|
||||
|
||||
procedure Destroy (Env : in out Lexical_Env_Access);
|
||||
|
||||
function Snaps_At_Start
|
||||
(Self : access ${root_node_value_type}'Class) return Boolean;
|
||||
|
||||
function Snaps_At_End
|
||||
(Self : access ${root_node_value_type}'Class) return Boolean;
|
||||
|
||||
-------------------
|
||||
-- Is_Token_Node --
|
||||
-------------------
|
||||
@@ -599,51 +605,56 @@ package body ${ada_lib_name}.Analysis.Implementation is
|
||||
(Node : access ${root_node_value_type}'Class;
|
||||
Snap : Boolean := False) return Source_Location_Range
|
||||
is
|
||||
TDH : Token_Data_Handler renames
|
||||
Convert (Node.Unit).TDH;
|
||||
Sloc_Start, Sloc_End : Source_Location;
|
||||
pragma Unreferenced (Snap);
|
||||
|
||||
type Token_Anchor is (T_Start, T_End);
|
||||
type Token_Pos is record
|
||||
Pos : Token_Index;
|
||||
Anchor : Token_Anchor;
|
||||
end record;
|
||||
|
||||
TDH : Token_Data_Handler renames Convert (Node.Unit).TDH;
|
||||
Token_Start, Token_End : Token_Pos;
|
||||
|
||||
function Get
|
||||
(Index : Token_Index) return Lexer.Token_Data_Type is
|
||||
(Index : Token_Index) return Lexer.Token_Data_Type
|
||||
is
|
||||
(Get_Token (TDH, Index));
|
||||
|
||||
function Sloc
|
||||
(T : Token_Pos) return Source_Location
|
||||
is
|
||||
(if T.Anchor = T_Start
|
||||
then Start_Sloc (Get (T.Pos).Sloc_Range)
|
||||
else End_Sloc (Get (T.Pos).Sloc_Range));
|
||||
|
||||
begin
|
||||
if Node.Is_Synthetic then
|
||||
return Sloc_Range (Node.Parent);
|
||||
end if;
|
||||
|
||||
-- Snapping: We'll go one token before the start token, and one token
|
||||
-- after the end token, and the sloc range will extend from the end of
|
||||
-- the start token to the start of the end token, including any
|
||||
-- whitespace and trivia that might be surrounding the node.
|
||||
--
|
||||
-- TODO: Only nodes we're gonna try to snap are nodes with default
|
||||
-- anchors. However, we can make the logic more specific, eg:
|
||||
--
|
||||
-- * If the start anchor is beginning, then snap the start sloc.
|
||||
--
|
||||
-- * If the end anchor is ending, then snap the end sloc.
|
||||
--
|
||||
-- This way composite cases can work too.
|
||||
|
||||
if Snap then
|
||||
declare
|
||||
Tok_Start : constant Token_Index :=
|
||||
Token_Index'Max (Node.Token_Start_Index - 1, 0);
|
||||
Tok_End : constant Token_Index :=
|
||||
Token_Index'Min (Node.Token_End_Index + 1, Last_Token (TDH));
|
||||
begin
|
||||
Sloc_Start := End_Sloc (Get (Tok_Start).Sloc_Range);
|
||||
Sloc_End := Start_Sloc (Get (Tok_End).Sloc_Range);
|
||||
end;
|
||||
if Node.Is_Ghost then
|
||||
Token_Start := (if Node.Token_Start_Index = 1
|
||||
then (1, T_Start)
|
||||
else (Node.Token_Start_Index - 1, T_End));
|
||||
Token_End := Token_Start;
|
||||
else
|
||||
Sloc_Start := Start_Sloc (Get (Node.Token_Start_Index).Sloc_Range);
|
||||
Sloc_End :=
|
||||
(if Node.Token_End_Index /= No_Token_Index
|
||||
then End_Sloc (Get (Node.Token_End_Index).Sloc_Range)
|
||||
else Start_Sloc (Get (Node.Token_Start_Index).Sloc_Range));
|
||||
Token_Start := (Node.Token_Start_Index, T_Start);
|
||||
Token_End := (Node.Token_End_Index, T_End);
|
||||
end if;
|
||||
return Make_Range (Sloc_Start, Sloc_End);
|
||||
|
||||
if Snaps_At_Start (Node)
|
||||
and then not Node.Is_Ghost
|
||||
and then Token_Start.Pos /= 1
|
||||
then
|
||||
Token_Start := (Token_Start.Pos - 1, T_End);
|
||||
end if;
|
||||
|
||||
if Snaps_At_End (Node) and then Token_End.Pos /= Last_Token (TDH) then
|
||||
Token_End := (Token_End.Pos + 1, T_Start);
|
||||
end if;
|
||||
|
||||
return Make_Range (Sloc (Token_Start), Sloc (Token_End));
|
||||
end Sloc_Range;
|
||||
|
||||
------------
|
||||
@@ -1065,6 +1076,40 @@ package body ${ada_lib_name}.Analysis.Implementation is
|
||||
</%self:case_dispatch>
|
||||
end Short_Image;
|
||||
|
||||
--------------------
|
||||
-- Snaps_At_Start --
|
||||
--------------------
|
||||
|
||||
function Snaps_At_Start
|
||||
(Self : access ${root_node_value_type}'Class) return Boolean is
|
||||
begin
|
||||
<%self:case_dispatch pred="${lambda n: n.snaps_at_start}">
|
||||
<%def name="action(node)">
|
||||
return True;
|
||||
</%def>
|
||||
<%def name="default()">
|
||||
return False;
|
||||
</%def>
|
||||
</%self:case_dispatch>
|
||||
end Snaps_At_Start;
|
||||
|
||||
------------------
|
||||
-- Snaps_At_End --
|
||||
------------------
|
||||
|
||||
function Snaps_At_End
|
||||
(Self : access ${root_node_value_type}'Class) return Boolean is
|
||||
begin
|
||||
<%self:case_dispatch pred="${lambda n: n.snaps_at_end}">
|
||||
<%def name="action(node)">
|
||||
return True;
|
||||
</%def>
|
||||
<%def name="default()">
|
||||
return Self.Is_Incomplete;
|
||||
</%def>
|
||||
</%self:case_dispatch>
|
||||
end Snaps_At_End;
|
||||
|
||||
-------------
|
||||
-- Parents --
|
||||
-------------
|
||||
|
||||
@@ -18,9 +18,9 @@ FileNode[1:1-7:1]
|
||||
| | | | | |default_value: <null>
|
||||
| | | | | SingleParam[2:13-2:14]
|
||||
| | | | | |is_varargs:
|
||||
| | | | | | VarArgsFlagAbsent[2:13-2:13]
|
||||
| | | | | | VarArgsFlagAbsent[2:12-2:12]
|
||||
| | | | | |is_kwargs:
|
||||
| | | | | | KwArgsFlagAbsent[2:13-2:13]
|
||||
| | | | | | KwArgsFlagAbsent[2:12-2:12]
|
||||
| | | | | |name:
|
||||
| | | | | | Id[2:13-2:14]: b
|
||||
| | | | | |default_value: <null>
|
||||
|
||||
@@ -8,16 +8,16 @@ main.py: Running...
|
||||
[PROPERTIES] Decl.internal_env_mappings_0 (<Decl main.txt:1:1-1:5>):
|
||||
[PROPERTIES] Result: (F_Key => "a", F_Val => <Decl main.txt:1:1-1:5>)
|
||||
[PROPERTIES] Decl.internal_md_1 (<Decl main.txt:1:6-1:12>):
|
||||
[PROPERTIES] HasPlus.p_as_bool (<HasPlusAbsent main.txt:1:6-1:6>):
|
||||
[PROPERTIES] HasPlusAbsent.p_as_bool (<HasPlusAbsent main.txt:1:6-1:6>):
|
||||
[PROPERTIES] HasPlus.p_as_bool (<HasPlusAbsent main.txt:1:5-1:5>):
|
||||
[PROPERTIES] HasPlusAbsent.p_as_bool (<HasPlusAbsent main.txt:1:5-1:5>):
|
||||
[PROPERTIES] Result: False
|
||||
[PROPERTIES] Result: False
|
||||
[PROPERTIES] Result: (F_B => False)
|
||||
[PROPERTIES] Decl.internal_env_mappings_0 (<Decl main.txt:1:6-1:12>):
|
||||
[PROPERTIES] Result: (F_Key => "b", F_Val => <Decl main.txt:1:6-1:12>)
|
||||
[PROPERTIES] Decl.internal_md_1 (<Decl main.txt:1:13-1:18>):
|
||||
[PROPERTIES] HasPlus.p_as_bool (<HasPlusPresent main.txt:1:13-1:13>):
|
||||
[PROPERTIES] HasPlusPresent.p_as_bool (<HasPlusPresent main.txt:1:13-1:13>):
|
||||
[PROPERTIES] HasPlus.p_as_bool (<HasPlusPresent main.txt:1:12-1:12>):
|
||||
[PROPERTIES] HasPlusPresent.p_as_bool (<HasPlusPresent main.txt:1:12-1:12>):
|
||||
[PROPERTIES] Result: True
|
||||
[PROPERTIES] Result: True
|
||||
[PROPERTIES] Result: (F_B => True)
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
Regular node: <Param 1:1-1:2>
|
||||
Regular node: <Name 1:1-1:2>
|
||||
Ghost node: <EnumDefault 2:1-2:1>
|
||||
Ghost node: <PlusQualifierAbsent 2:1-2:1>
|
||||
Ghost node: <EnumDefault 1:2-1:2>
|
||||
Ghost node: <PlusQualifierAbsent 1:2-1:2>
|
||||
Regular node: <Param 2:1-2:7>
|
||||
Regular node: <Name 2:1-2:2>
|
||||
Regular node: <EnumNull 2:3-2:7>
|
||||
Ghost node: <PlusQualifierAbsent 3:1-3:1>
|
||||
Ghost node: <PlusQualifierAbsent 2:7-2:7>
|
||||
Regular node: <Param 3:1-3:2>
|
||||
Regular node: <Name 3:1-3:2>
|
||||
Ghost node: <EnumDefault 4:1-4:1>
|
||||
Ghost node: <PlusQualifierAbsent 4:1-4:1>
|
||||
Ghost node: <EnumDefault 3:2-3:2>
|
||||
Ghost node: <PlusQualifierAbsent 3:2-3:2>
|
||||
Regular node: <Param 4:1-4:7>
|
||||
Regular node: <Name 4:1-4:2>
|
||||
Regular node: <EnumNull 4:3-4:7>
|
||||
Ghost node: <PlusQualifierAbsent 4:8-4:8>
|
||||
Ghost node: <PlusQualifierAbsent 4:7-4:7>
|
||||
Done.
|
||||
Done
|
||||
|
||||
Reference in New Issue
Block a user