Commits

Kirill Simonov committed e51f297

Refactor resolver.

Comments (0)

Files changed (24)

lib/yaml/__init__.py

 from serializer import *
 from representer import *
 
-from detector import *
+from resolver import *
 
 from tokens import *
 from events import *
     """
     return dump_all([data], stream, Dumper=SafeDumper, **kwds)
 
-def add_detector(tag, regexp, first=None, Loader=Loader, Dumper=Dumper):
+def add_implicit_detector(tag, regexp, first=None,
+        Loader=Loader, Dumper=Dumper):
     """
     Add an implicit scalar detector.
     If an implicit scalar value matches the given regexp,
     the corresponding tag is assigned to the scalar.
     first is a sequence of possible initial characters or None.
     """
-    Loader.add_detector(tag, regexp, first)
-    Dumper.add_detector(tag, regexp, first)
+    Loader.add_implicit_resolver(tag, regexp, first)
+    Dumper.add_implicit_resolver(tag, regexp, first)
 
-def add_resolver(tag, path, Loader=Loader):
+def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper):
     """
     Add a path based resolver for the given tag.
     A path is a list of keys that forms a path
     to a node in the representation tree.
     Keys can be string values, integers, or None.
     """
-    Loader.add_resolver(tag, path)
+    Loader.add_path_resolver(tag, path, kind)
+    Dumper.add_path_resolver(tag, path, kind)
 
 def add_constructor(tag, constructor, Loader=Loader):
     """
     """
     Loader.add_multi_constructor(tag_prefix, multi_constructor)
 
+def add_representer(data_type, representer, Dumper=Dumper):
+    """
+    Add a representer for the given type.
+    Representer is a function accepting a Dumper instance
+    and an instance of the given data type
+    and producing the corresponding representation node.
+    """
+    Dumper.add_representer(data_type, representer)
+
 class YAMLObjectMetaclass(type):
     """
     The metaclass for YAMLObject.

lib/yaml/composer.py

 
-__all__ = ['BaseComposer', 'Composer', 'ComposerError']
+__all__ = ['Composer', 'ComposerError']
 
 from error import MarkedYAMLError
 from events import *
 class ComposerError(MarkedYAMLError):
     pass
 
-class BaseComposer:
-
-    yaml_resolvers = {}
+class Composer:
 
     def __init__(self):
         self.all_anchors = {}
         self.complete_anchors = {}
-        self.resolver_tags = []
-        self.resolver_paths = []
 
     def check_node(self):
         # If there are more documents available?
         self.get_event()
 
         # Compose the root node.
-        node = self.compose_node([])
+        node = self.compose_node(None, None)
 
         # Drop the DOCUMENT-END event.
         self.get_event()
 
         self.all_anchors = {}
         self.complete_anchors = {}
-        self.resolver_tags = []
-        self.resolver_paths = []
         return node
 
-    def increase_resolver_depth(self, path):
-        depth = len(path)
-        tag = None
-        paths = []
-        if not depth:
-            for resolver_path in self.yaml_resolvers.keys():
-                if resolver_path:
-                    paths.append(resolver_path)
-                else:
-                    tag = self.yaml_resolvers[resolver_path]
-        else:
-            base, index = path[-1]
-            if isinstance(index, ScalarNode)    \
-                    and index.tag == self.DEFAULT_SCALAR_TAG:
-                index = index.value
-            elif isinstance(index, Node):
-                index = None
-            for resolver_path in self.resolver_paths[-1]:
-                resolver_index = resolver_path[depth-1]
-                if resolver_index is None or resolver_index == index:
-                    if len(resolver_index) > depth:
-                        paths.append(resolver_path)
-                    else:
-                        tag = self.yaml_resolvers[resolver_path]
-        self.resolver_tags.append(tag)
-        self.resolver_paths.append(paths)
-
-    def decrease_resolver_depth(self):
-        del self.resolver_tags[-1]
-        del self.resolver_paths[-1]
-
-    def compose_node(self, path):
+    def compose_node(self, parent, index):
         if self.check_event(AliasEvent):
             event = self.get_event()
             anchor = event.anchor
                         "found recursive anchor %r" % anchor.encode('utf-8'),
                         event.start_mark)
             return self.complete_anchors[anchor]
-        self.increase_resolver_depth(path)
         event = self.peek_event()
         anchor = event.anchor
         if anchor is not None:
                         % anchor.encode('utf-8'), self.all_anchors[anchor].start_mark,
                         "second occurence", event.start_mark)
             self.all_anchors[anchor] = event
+        self.descend_resolver(parent, index)
         if self.check_event(ScalarEvent):
-            node = self.compose_scalar_node(path)
+            node = self.compose_scalar_node()
         elif self.check_event(SequenceStartEvent):
-            node = self.compose_sequence_node(path)
+            node = self.compose_sequence_node()
         elif self.check_event(MappingStartEvent):
-            node = self.compose_mapping_node(path)
+            node = self.compose_mapping_node()
         if anchor is not None:
             self.complete_anchors[anchor] = node
-        self.decrease_resolver_depth()
+        self.ascend_resolver()
         return node
 
-    def compose_scalar_node(self, path):
+    def compose_scalar_node(self):
         event = self.get_event()
-        tag = self.resolve_scalar(path, event.tag, event.implicit, event.value)
+        tag = event.tag
+        if tag is None or tag == u'!':
+            tag = self.resolve(ScalarNode, event.value, event.implicit)
         return ScalarNode(tag, event.value,
                 event.start_mark, event.end_mark, style=event.style)
 
-    def compose_sequence_node(self, path):
+    def compose_sequence_node(self):
         start_event = self.get_event()
-        tag = self.resolve_sequence(path, start_event.tag)
+        tag = start_event.tag
+        if tag is None or tag == u'!':
+            tag = self.resolve(SequenceNode, None, start_event.implicit)
         node = SequenceNode(tag, [],
                 start_event.start_mark, None,
                 flow_style=start_event.flow_style)
         index = 0
         while not self.check_event(SequenceEndEvent):
-            node.value.append(self.compose_node(path+[(node, index)]))
+            node.value.append(self.compose_node(node, index))
             index += 1
         end_event = self.get_event()
         node.end_mark = end_event.end_mark
         return node
 
-    def compose_mapping_node(self, path):
+    def compose_mapping_node(self):
         start_event = self.get_event()
-        tag = self.resolve_mapping(path, start_event.tag)
+        tag = start_event.tag
+        if tag is None or tag == u'!':
+            tag = self.resolve(MappingNode, None, start_event.implicit)
         node = MappingNode(tag, {},
                 start_event.start_mark, None,
                 flow_style=start_event.flow_style)
         while not self.check_event(MappingEndEvent):
             key_event = self.peek_event()
-            item_key = self.compose_node(path+[(node, None)])
-            item_value = self.compose_node(path+[(node, item_key)])
+            item_key = self.compose_node(node, None)
             if item_key in node.value:
                 raise ComposerError("while composing a mapping", start_event.start_mark,
                         "found duplicate key", key_event.start_mark)
+            item_value = self.compose_node(node, item_key)
             node.value[item_key] = item_value
         end_event = self.get_event()
         node.end_mark = end_event.end_mark
         return node
 
-    def resolve_scalar(self, path, tag, implicit, value):
-        if implicit:
-            tag = self.detect(value)
-        if tag is None and self.resolver_tags[-1]:
-            tag = self.resolver_tags[-1]
-        if tag is None or tag == u'!':
-            tag = self.DEFAULT_SCALAR_TAG
-        return tag
-
-    def resolve_sequence(self, path, tag):
-        if tag is None and self.resolver_tags[-1]:
-            tag = self.resolver_tags[-1]
-        if tag is None or tag == u'!':
-            tag = self.DEFAULT_SEQUENCE_TAG
-        return tag
-
-    def resolve_mapping(self, path, tag):
-        if tag is None and self.resolver_tags[-1]:
-            tag = self.resolver_tags[-1]
-        if tag is None or tag == u'!':
-            tag = self.DEFAULT_MAPPING_TAG
-        return tag
-
-    def add_resolver(self, tag, path):
-        if not 'yaml_resolvers' in cls.__dict__:
-            cls.yaml_resolvers = cls.yaml_resolvers.copy()
-        cls.yaml_resolvers[tuple(path)] = tag
-    add_resolver = classmethod(add_resolver)
-
-class Composer(BaseComposer):
-    pass
-

lib/yaml/detector.py

-
-__all__ = ['BaseDetector', 'Detector']
-
-import re
-
-class BaseDetector:
-
-    DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
-    DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq'
-    DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map'
-
-    yaml_detectors = {}
-
-    def __init__(self):
-        pass
-
-    def add_detector(cls, tag, regexp, first):
-        if not 'yaml_detectors' in cls.__dict__:
-            cls.yaml_detectors = cls.yaml_detectors.copy()
-        for ch in first:
-            cls.yaml_detectors.setdefault(ch, []).append((tag, regexp))
-    add_detector = classmethod(add_detector)
-
-    def detect(self, value):
-        if value == u'':
-            detectors = self.yaml_detectors.get(u'', [])
-        else:
-            detectors = self.yaml_detectors.get(value[0], [])
-        detectors += self.yaml_detectors.get(None, [])
-        for tag, regexp in detectors:
-            if regexp.match(value):
-                return tag
-
-class Detector(BaseDetector):
-    pass
-
-Detector.add_detector(
-        u'tag:yaml.org,2002:bool',
-        re.compile(ur'''^(?:yes|Yes|YES|n|N|no|No|NO
-                    |true|True|TRUE|false|False|FALSE
-                    |on|On|ON|off|Off|OFF)$''', re.X),
-        list(u'yYnNtTfFoO'))
-
-Detector.add_detector(
-        u'tag:yaml.org,2002:float',
-        re.compile(ur'''^(?:[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*(?:[eE][-+][0-9]+)?
-                    |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
-                    |[-+]?\.(?:inf|Inf|INF)
-                    |\.(?:nan|NaN|NAN))$''', re.X),
-        list(u'-+0123456789.'))
-
-Detector.add_detector(
-        u'tag:yaml.org,2002:int',
-        re.compile(ur'''^(?:[-+]?0b[0-1_]+
-                    |[-+]?0[0-7_]+
-                    |[-+]?(?:0|[1-9][0-9_]*)
-                    |[-+]?0x[0-9a-fA-F_]+
-                    |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
-        list(u'-+0123456789'))
-
-Detector.add_detector(
-        u'tag:yaml.org,2002:merge',
-        re.compile(ur'^(?:<<)$'),
-        ['<'])
-
-Detector.add_detector(
-        u'tag:yaml.org,2002:null',
-        re.compile(ur'''^(?: ~
-                    |null|Null|NULL
-                    | )$''', re.X),
-        [u'~', u'n', u'N', u''])
-
-Detector.add_detector(
-        u'tag:yaml.org,2002:timestamp',
-        re.compile(ur'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
-                    |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
-                     (?:[Tt]|[ \t]+)[0-9][0-9]?
-                     :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)?
-                     (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
-        list(u'0123456789'))
-
-Detector.add_detector(
-        u'tag:yaml.org,2002:value',
-        re.compile(ur'^(?:=)$'),
-        ['='])
-
-# The following detector is only for documentation purposes. It cannot work
-# because plain scalars cannot start with '!', '&', or '*'.
-Detector.add_detector(
-        u'tag:yaml.org,2002:yaml',
-        re.compile(ur'^(?:!|&|\*)$'),
-        list(u'!&*'))
-

lib/yaml/dumper.py

 from emitter import *
 from serializer import *
 from representer import *
-from detector import *
+from resolver import *
 
-class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseDetector):
+class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver):
 
     def __init__(self, stream,
             canonical=None, indent=None, width=None,
                 explicit_start=explicit_start, explicit_end=explicit_end,
                 version=version, tags=tags)
         Representer.__init__(self)
-        Detector.__init__(self)
+        Resolver.__init__(self)
 
-class SafeDumper(Emitter, Serializer, SafeRepresenter, Detector):
+class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver):
 
     def __init__(self, stream,
             canonical=None, indent=None, width=None,
                 explicit_start=explicit_start, explicit_end=explicit_end,
                 version=version, tags=tags)
         SafeRepresenter.__init__(self)
-        Detector.__init__(self)
+        Resolver.__init__(self)
 
-class Dumper(Emitter, Serializer, Representer, Detector):
+class Dumper(Emitter, Serializer, Representer, Resolver):
 
     def __init__(self, stream,
             canonical=None, indent=None, width=None,
                 explicit_start=explicit_start, explicit_end=explicit_end,
                 version=version, tags=tags)
         Representer.__init__(self)
-        Detector.__init__(self)
+        Resolver.__init__(self)
 

lib/yaml/emitter.py

         if isinstance(self.event, ScalarEvent):
             if self.style is None:
                 self.style = self.choose_scalar_style()
-            if self.style == '':
+            if ((not self.canonical or tag is None) and
+                ((self.style == '' and self.event.implicit[0])
+                        or (self.style != '' and self.event.implicit[1]))):
                 self.prepared_tag = None
                 return
-            if self.event.implicit and not tag:
+            if self.event.implicit[0] and not tag:
                 tag = u'!'
                 self.prepared_tag = None
+        else:
+            if (not self.canonical or tag is None) and self.event.implicit:
+                self.prepared_tag = None
+                return
         if not tag:
-            self.prepared_tag = None
-            return
+            raise EmitterError("tag is not specified")
         if self.prepared_tag is None:
             self.prepared_tag = self.prepare_tag(tag)
         if self.prepared_tag:
             self.analysis = self.analyze_scalar(self.event.value)
         if self.event.style == '"' or self.canonical:
             return '"'
-        if not self.event.style and self.event.implicit:
+        if not self.event.style and self.event.implicit[0]:
             if (not (self.simple_key_context and
                     (self.analysis.empty or self.analysis.multiline))
                 and (self.flow_level and self.analysis.allow_flow_plain

lib/yaml/events.py

         self.start_mark = start_mark
         self.end_mark = end_mark
     def __repr__(self):
-        attributes = [key for key in ['anchor', 'tag', 'value']
+        attributes = [key for key in ['anchor', 'tag', 'implicit', 'value']
                 if hasattr(self, key)]
         arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
                 for key in attributes])
         self.end_mark = end_mark
 
 class CollectionStartEvent(NodeEvent):
-    def __init__(self, anchor, tag, start_mark=None, end_mark=None,
+    def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None,
             flow_style=None):
         self.anchor = anchor
         self.tag = tag
+        self.implicit = implicit
         self.start_mark = start_mark
         self.end_mark = end_mark
         self.flow_style = flow_style

lib/yaml/loader.py

 from parser import *
 from composer import *
 from constructor import *
-from detector import *
+from resolver import *
 
-class BaseLoader(Reader, Scanner, Parser,
-        BaseComposer, BaseConstructor, BaseDetector):
+class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver):
 
     def __init__(self, stream):
         Reader.__init__(self, stream)
         Scanner.__init__(self)
         Parser.__init__(self)
-        BaseComposer.__init__(self)
+        Composer.__init__(self)
         BaseConstructor.__init__(self)
-        BaseDetector.__init__(self)
+        BaseResolver.__init__(self)
 
-class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Detector):
+class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver):
 
     def __init__(self, stream):
         Reader.__init__(self, stream)
         Parser.__init__(self)
         Composer.__init__(self)
         SafeConstructor.__init__(self)
-        Detector.__init__(self)
+        Resolver.__init__(self)
 
-class Loader(Reader, Scanner, Parser, Composer, Constructor, Detector):
+class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver):
 
     def __init__(self, stream):
         Reader.__init__(self, stream)
         Parser.__init__(self)
         Composer.__init__(self)
         Constructor.__init__(self)
-        Detector.__init__(self)
+        Resolver.__init__(self)
 

lib/yaml/parser.py

                 start_mark = end_mark = self.peek_token().start_mark
             event = None
             collection_events = None
+            implicit = (tag is None or tag == u'!')
             if indentless_sequence and self.check_token(BlockEntryToken):
                 end_mark = self.peek_token().end_mark
-                event = SequenceStartEvent(anchor, tag, start_mark, end_mark)
+                event = SequenceStartEvent(anchor, tag, implicit,
+                        start_mark, end_mark)
                 collection_events = self.parse_indentless_sequence()
             else:
                 if self.check_token(ScalarToken):
                     token = self.get_token()
                     end_mark = token.end_mark
-                    implicit = ((tag is None or tag == u'!') and token.implicit)
+                    if (token.plain and tag is None) or tag == u'!':
+                        implicit = (True, False)
+                    elif tag is None:
+                        implicit = (False, True)
+                    else:
+                        implicit = (False, False)
                     event = ScalarEvent(anchor, tag, implicit, token.value,
                             start_mark, end_mark, style=token.style)
                 elif self.check_token(FlowSequenceStartToken):
                     end_mark = self.peek_token().end_mark
-                    event = SequenceStartEvent(anchor, tag, start_mark, end_mark,
-                            flow_style=True)
+                    event = SequenceStartEvent(anchor, tag, implicit,
+                            start_mark, end_mark, flow_style=True)
                     collection_events = self.parse_flow_sequence()
                 elif self.check_token(FlowMappingStartToken):
                     end_mark = self.peek_token().end_mark
-                    event = MappingStartEvent(anchor, tag, start_mark, end_mark,
-                            flow_style=True)
+                    event = MappingStartEvent(anchor, tag, implicit,
+                            start_mark, end_mark, flow_style=True)
                     collection_events = self.parse_flow_mapping()
                 elif block and self.check_token(BlockSequenceStartToken):
                     end_mark = self.peek_token().start_mark
-                    event = SequenceStartEvent(anchor, tag, start_mark, end_mark,
-                            flow_style=False)
+                    event = SequenceStartEvent(anchor, tag, implicit,
+                            start_mark, end_mark, flow_style=False)
                     collection_events = self.parse_block_sequence()
                 elif block and self.check_token(BlockMappingStartToken):
                     end_mark = self.peek_token().start_mark
-                    event = MappingStartEvent(anchor, tag, start_mark, end_mark,
-                            flow_style=False)
+                    event = MappingStartEvent(anchor, tag, implicit,
+                            start_mark, end_mark, flow_style=False)
                     collection_events = self.parse_block_mapping()
                 elif anchor is not None or tag is not None:
                     # Empty scalars are allowed even if a tag or an anchor is
                     # specified.
-                    implicit = (tag is None or tag == u'!')
-                    event = ScalarEvent(anchor, tag, implicit, u'',
+                    event = ScalarEvent(anchor, tag, (implicit, False), u'',
                             start_mark, end_mark)
                 else:
                     if block:
         while not self.check_token(FlowSequenceEndToken):
             if self.check_token(KeyToken):
                 token = self.get_token()
-                yield MappingStartEvent(None, None, # u'!',
+                yield MappingStartEvent(None, None, True,
                         token.start_mark, token.end_mark,
                         flow_style=True)
                 if not self.check_token(ValueToken,
         yield MappingEndEvent(token.start_mark, token.end_mark)
 
     def process_empty_scalar(self, mark):
-        return ScalarEvent(None, None, True, u'', mark, mark)
+        return ScalarEvent(None, None, (True, False), u'', mark, mark)
 

lib/yaml/representer.py

 
 from error import *
 from nodes import *
-from detector import *
 
 try:
     import datetime

lib/yaml/resolver.py

+
+__all__ = ['BaseResolver', 'Resolver']
+
+from error import *
+from nodes import *
+
+import re
+
+class ResolverError(YAMLError):
+    pass
+
+class BaseResolver:
+
+    DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
+    DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq'
+    DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map'
+
+    yaml_implicit_resolvers = {}
+    yaml_path_resolvers = {}
+
+    def __init__(self):
+        self.resolver_exact_paths = []
+        self.resolver_prefix_paths = []
+
+    def add_implicit_resolver(cls, tag, regexp, first):
+        if not 'yaml_implicit_resolvers' in cls.__dict__:
+            cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy()
+        for ch in first:
+            cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp))
+    add_implicit_resolver = classmethod(add_implicit_resolver)
+
+    def add_path_resolver(cls, tag, path, kind=None):
+        if not 'yaml_path_resolvers' in cls.__dict__:
+            cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy()
+        new_path = []
+        for element in path:
+            if isinstance(element, (list, tuple)):
+                if len(element) == 2:
+                    node_check, index_check = element
+                elif len(element) == 1:
+                    node_check = element[0]
+                    index_check = True
+                else:
+                    raise ResolverError("Invalid path element: %s" % element)
+            else:
+                node_check = None
+                index_check = element
+            if node_check is str:
+                node_check = ScalarNode
+            elif node_check is list:
+                node_check = SequenceNode
+            elif node_check is map:
+                node_check = MappingNode
+            elif node_check not in [ScalarNode, SequenceNode, MappingNode]  \
+                    and not isinstance(node_check, basestring)  \
+                    and node_check is not None:
+                raise ResolverError("Invalid node checker: %s" % node_check)
+            if not isinstance(index_check, (basestring, int))   \
+                    and index_check is not None:
+                raise ResolverError("Invalid index checker: %s" % index_check)
+            new_path.append((node_check, index_check))
+        if kind is str:
+            kind = ScalarNode
+        elif kind is list:
+            kind = SequenceNode
+        elif kind is map:
+            kind = MappingNode
+        elif kind not in [ScalarNode, SequenceNode, MappingNode]    \
+                and kind is not None:
+            raise ResolverError("Invalid node kind: %s" % kind)
+        cls.yaml_path_resolvers[tuple(new_path), kind] = tag
+    add_path_resolver = classmethod(add_path_resolver)
+
+    def descend_resolver(self, current_node, current_index):
+        exact_paths = {}
+        prefix_paths = []
+        if current_node:
+            depth = len(self.resolver_prefix_paths)
+            for path, kind in self.resolver_prefix_paths[-1]:
+                if self.check_resolver_prefix(depth, path, kind,
+                        current_node, current_index):
+                    if len(path) > depth:
+                        prefix_paths.append((path, kind))
+                    else:
+                        exact_paths[kind] = self.yaml_path_resolvers[path, kind]
+        else:
+            for path, kind in self.yaml_path_resolvers:
+                if not path:
+                    exact_paths[kind] = self.yaml_path_resolvers[path, kind]
+                else:
+                    prefix_paths.append((path, kind))
+        self.resolver_exact_paths.append(exact_paths)
+        self.resolver_prefix_paths.append(prefix_paths)
+
+    def ascend_resolver(self):
+        self.resolver_exact_paths.pop()
+        self.resolver_prefix_paths.pop()
+
+    def check_resolver_prefix(self, depth, path, kind,
+            current_node, current_index):
+        node_check, index_check = path[depth-1]
+        if isinstance(node_check, basestring):
+            if current_node.tag != node_check:
+                return
+        elif node_check is not None:
+            if not isinstance(current_node, node_check):
+                return
+        if index_check is True and current_index is not None:
+            return
+        if index_check in [False, None] and current_index is None:
+            return
+        if isinstance(index_check, basestring):
+            if not (isinstance(current_index, ScalarNode)
+                    and index_check == current_index.value):
+                return
+        elif isinstance(index_check, int):
+            if index_check != current_index:
+                return
+        return True
+
+    def resolve(self, kind, value, implicit):
+        if kind is ScalarNode and implicit[0]:
+            if value == u'':
+                resolvers = self.yaml_implicit_resolvers.get(u'', [])
+            else:
+                resolvers = self.yaml_implicit_resolvers.get(value[0], [])
+            resolvers += self.yaml_implicit_resolvers.get(None, [])
+            for tag, regexp in resolvers:
+                if regexp.match(value):
+                    return tag
+            implicit = implicit[1]
+        exact_paths = self.resolver_exact_paths[-1]
+        if kind in exact_paths:
+            return exact_paths[kind]
+        if None in exact_paths:
+            return exact_paths[None]
+        if kind is ScalarNode:
+            return self.DEFAULT_SCALAR_TAG
+        elif kind is SequenceNode:
+            return self.DEFAULT_SEQUENCE_TAG
+        elif kind is MappingNode:
+            return self.DEFAULT_MAPPING_TAG
+
+class Resolver(BaseResolver):
+    pass
+
+Resolver.add_implicit_resolver(
+        u'tag:yaml.org,2002:bool',
+        re.compile(ur'''^(?:yes|Yes|YES|n|N|no|No|NO
+                    |true|True|TRUE|false|False|FALSE
+                    |on|On|ON|off|Off|OFF)$''', re.X),
+        list(u'yYnNtTfFoO'))
+
+Resolver.add_implicit_resolver(
+        u'tag:yaml.org,2002:float',
+        re.compile(ur'''^(?:[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*(?:[eE][-+][0-9]+)?
+                    |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*
+                    |[-+]?\.(?:inf|Inf|INF)
+                    |\.(?:nan|NaN|NAN))$''', re.X),
+        list(u'-+0123456789.'))
+
+Resolver.add_implicit_resolver(
+        u'tag:yaml.org,2002:int',
+        re.compile(ur'''^(?:[-+]?0b[0-1_]+
+                    |[-+]?0[0-7_]+
+                    |[-+]?(?:0|[1-9][0-9_]*)
+                    |[-+]?0x[0-9a-fA-F_]+
+                    |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X),
+        list(u'-+0123456789'))
+
+Resolver.add_implicit_resolver(
+        u'tag:yaml.org,2002:merge',
+        re.compile(ur'^(?:<<)$'),
+        ['<'])
+
+Resolver.add_implicit_resolver(
+        u'tag:yaml.org,2002:null',
+        re.compile(ur'''^(?: ~
+                    |null|Null|NULL
+                    | )$''', re.X),
+        [u'~', u'n', u'N', u''])
+
+Resolver.add_implicit_resolver(
+        u'tag:yaml.org,2002:timestamp',
+        re.compile(ur'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
+                    |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
+                     (?:[Tt]|[ \t]+)[0-9][0-9]?
+                     :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)?
+                     (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
+        list(u'0123456789'))
+
+Resolver.add_implicit_resolver(
+        u'tag:yaml.org,2002:value',
+        re.compile(ur'^(?:=)$'),
+        ['='])
+
+# The following resolver is only for documentation purposes. It cannot work
+# because plain scalars cannot start with '!', '&', or '*'.
+Resolver.add_implicit_resolver(
+        u'tag:yaml.org,2002:yaml',
+        re.compile(ur'^(?:!|&|\*)$'),
+        list(u'!&*'))
+

lib/yaml/serializer.py

         self.emit(DocumentStartEvent(explicit=self.use_explicit_start,
             version=self.use_version, tags=self.use_tags))
         self.anchor_node(node)
-        self.serialize_node(node)
+        self.serialize_node(node, None, None)
         self.emit(DocumentEndEvent(explicit=self.use_explicit_end))
         self.serialized_nodes = {}
         self.anchors = {}
         self.last_anchor_id += 1
         return self.ANCHOR_TEMPLATE % self.last_anchor_id
 
-    def serialize_node(self, node):
+    def serialize_node(self, node, parent, index):
         alias = self.anchors[node]
         if node in self.serialized_nodes:
             self.emit(AliasEvent(alias))
         else:
             self.serialized_nodes[node] = True
+            self.descend_resolver(parent, index)
             if isinstance(node, ScalarNode):
-                detected_tag = self.detect(node.value)
-                implicit = (node.tag == self.detect(node.value)
-                        or (node.tag == self.DEFAULT_SCALAR_TAG
-                            and detected_tag is None))
+                detected_tag = self.resolve(ScalarNode, node.value, (True, False))
+                default_tag = self.resolve(ScalarNode, node.value, (False, True))
+                implicit = (node.tag == detected_tag), (node.tag == default_tag)
                 self.emit(ScalarEvent(alias, node.tag, implicit, node.value,
                     style=node.style))
             elif isinstance(node, SequenceNode):
-                # TODO:
-                # 1) Check the current path in the Resolver.
-                # 2) Add the implicit flag to the SequenceStartEvent and
-                # MappingStartEvent.
-                tag = node.tag
-                if tag == self.DEFAULT_SEQUENCE_TAG and not self.canonical:
-                    tag = None
-                self.emit(SequenceStartEvent(alias, tag,
+                implicit = (node.tag
+                            == self.resolve(SequenceNode, node.value, True))
+                self.emit(SequenceStartEvent(alias, node.tag, implicit,
                     flow_style=node.flow_style))
+                index = 0
                 for item in node.value:
-                    self.serialize_node(item)
+                    self.serialize_node(item, node, index)
+                    index += 1
                 self.emit(SequenceEndEvent())
             elif isinstance(node, MappingNode):
-                tag = node.tag
-                if tag == self.DEFAULT_MAPPING_TAG and not self.canonical:
-                    tag = None
-                self.emit(MappingStartEvent(alias, tag,
+                implicit = (node.tag
+                            == self.resolve(MappingNode, node.value, True))
+                self.emit(MappingStartEvent(alias, node.tag, implicit,
                     flow_style=node.flow_style))
                 if hasattr(node.value, 'keys'):
                     for key in node.value.keys():
-                        self.serialize_node(key)
-                        self.serialize_node(node.value[key])
+                        self.serialize_node(key, node, None)
+                        self.serialize_node(node.value[key], node, key)
                 else:
                     for key, value in node.value:
-                        self.serialize_node(key)
-                        self.serialize_node(value)
+                        self.serialize_node(key, node, None)
+                        self.serialize_node(value, node, key)
                 self.emit(MappingEndEvent())
+            self.ascend_resolver()
 

lib/yaml/tokens.py

 
 class ScalarToken(Token):
     id = '<scalar>'
-    def __init__(self, value, implicit, start_mark, end_mark, style=None):
+    def __init__(self, value, plain, start_mark, end_mark, style=None):
         self.value = value
-        self.implicit = implicit
+        self.plain = plain
         self.start_mark = start_mark
         self.end_mark = end_mark
         self.style = style

tests/data/documents.events

 - !StreamStart
 - !DocumentStart { explicit: false }
-- !Scalar { implicit: true, value: 'data' }
+- !Scalar { implicit: [true,false], value: 'data' }
 - !DocumentEnd
 - !DocumentStart
-- !Scalar { implicit: true }
+- !Scalar { implicit: [true,false] }
 - !DocumentEnd
 - !DocumentStart { version: [1,1], tags: { '!': '!foo', '!yaml!': 'tag:yaml.org,2002:', '!ugly!': '!!!!!!!' } }
-- !Scalar { implicit: true }
+- !Scalar { implicit: [true,false] }
 - !DocumentEnd
 - !StreamEnd

tests/data/mappings.events

 
 - !DocumentStart
 - !MappingStart
-- !Scalar { implicit: true, value: 'key' }
-- !Scalar { implicit: true, value: 'value' }
-- !Scalar { implicit: true, value: 'empty mapping' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'value' }
+- !Scalar { implicit: [true,true], value: 'empty mapping' }
 - !MappingStart
 - !MappingEnd
-- !Scalar { implicit: true, value: 'empty mapping with tag' }
-- !MappingStart { tag: '!mytag' }
+- !Scalar { implicit: [true,true], value: 'empty mapping with tag' }
+- !MappingStart { tag: '!mytag', implicit: false }
 - !MappingEnd
-- !Scalar { implicit: true, value: 'block mapping' }
+- !Scalar { implicit: [true,true], value: 'block mapping' }
 - !MappingStart
 - !MappingStart
-- !Scalar { implicit: true, value: 'complex' }
-- !Scalar { implicit: true, value: 'key' }
-- !Scalar { implicit: true, value: 'complex' }
-- !Scalar { implicit: true, value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
 - !MappingEnd
 - !MappingStart
-- !Scalar { implicit: true, value: 'complex' }
-- !Scalar { implicit: true, value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
 - !MappingEnd
 - !MappingEnd
-- !Scalar { implicit: true, value: 'flow mapping' }
+- !Scalar { implicit: [true,true], value: 'flow mapping' }
 - !MappingStart { flow_style: true }
-- !Scalar { implicit: true, value: 'key' }
-- !Scalar { implicit: true, value: 'value' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'value' }
 - !MappingStart
-- !Scalar { implicit: true, value: 'complex' }
-- !Scalar { implicit: true, value: 'key' }
-- !Scalar { implicit: true, value: 'complex' }
-- !Scalar { implicit: true, value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
 - !MappingEnd
 - !MappingStart
-- !Scalar { implicit: true, value: 'complex' }
-- !Scalar { implicit: true, value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
 - !MappingEnd
 - !MappingEnd
 - !MappingEnd

tests/data/resolver.data

+---
+"this scalar should be selected"
+---
+key11: !foo
+    key12:
+        is: [selected]
+    key22:
+        key13: [not, selected]
+        key23: [not, selected]
+    key32:
+        key31: [not, selected]
+        key32: [not, selected]
+        key33: {not: selected}
+key21: !bar
+    - not selected
+    - selected
+    - not selected
+key31: !baz
+    key12:
+        key13:
+            key14: {selected}
+        key23:
+            key14: [not, selected]
+        key33:
+            key14: {selected}
+            key24: {not: selected}
+    key22:
+        -   key14: {selected}
+            key24: {not: selected}
+        -   key14: {selected}

tests/data/resolver.path

+--- !root/scalar
+"this scalar should be selected"
+--- !root
+key11: !foo
+    key12: !root/key11/key12/*
+        is: [selected]
+    key22:
+        key13: [not, selected]
+        key23: [not, selected]
+    key32:
+        key31: [not, selected]
+        key32: [not, selected]
+        key33: {not: selected}
+key21: !bar
+    - not selected
+    - !root/key21/1/* selected
+    - not selected
+key31: !baz
+    key12:
+        key13:
+            key14: !root/key31/*/*/key14/map {selected}
+        key23:
+            key14: [not, selected]
+        key33:
+            key14: !root/key31/*/*/key14/map {selected}
+            key24: {not: selected}
+    key22:
+        -   key14: !root/key31/*/*/key14/map {selected}
+            key24: {not: selected}
+        -   key14: !root/key31/*/*/key14/map {selected}

tests/data/scalars.events

 
 - !DocumentStart
 - !MappingStart
-- !Scalar { implicit: true, value: 'empty scalar' }
-- !Scalar { implicit: true, value: '' }
-- !Scalar { implicit: true, value: 'implicit scalar' }
-- !Scalar { implicit: true, value: 'data' }
-- !Scalar { implicit: true, value: 'quoted scalar' }
+- !Scalar { implicit: [true,true], value: 'empty scalar' }
+- !Scalar { implicit: [true,false], value: '' }
+- !Scalar { implicit: [true,true], value: 'implicit scalar' }
+- !Scalar { implicit: [true,true], value: 'data' }
+- !Scalar { implicit: [true,true], value: 'quoted scalar' }
 - !Scalar { value: 'data', style: '"' }
-- !Scalar { implicit: true, value: 'block scalar' }
+- !Scalar { implicit: [true,true], value: 'block scalar' }
 - !Scalar { value: 'data', style: '|' }
-- !Scalar { implicit: true, value: 'empty scalar with tag' }
-- !Scalar { implicit: true, tag: '!mytag', value: '' }
-- !Scalar { implicit: true, value: 'implicit scalar with tag' }
-- !Scalar { implicit: true, tag: '!mytag', value: 'data' }
-- !Scalar { implicit: true, value: 'quoted scalar with tag' }
-- !Scalar { value: 'data', style: '"', tag: '!mytag' }
-- !Scalar { implicit: true, value: 'block scalar with tag' }
-- !Scalar { value: 'data', style: '|', tag: '!mytag' }
-- !Scalar { implicit: true, value: 'single character' }
-- !Scalar { value: 'a', implicit: true }
-- !Scalar { implicit: true, value: 'single digit' }
-- !Scalar { value: '1', implicit: true }
+- !Scalar { implicit: [true,true], value: 'empty scalar with tag' }
+- !Scalar { implicit: [false,false], tag: '!mytag', value: '' }
+- !Scalar { implicit: [true,true], value: 'implicit scalar with tag' }
+- !Scalar { implicit: [false,false], tag: '!mytag', value: 'data' }
+- !Scalar { implicit: [true,true], value: 'quoted scalar with tag' }
+- !Scalar { value: 'data', style: '"', tag: '!mytag', implicit: [false,false] }
+- !Scalar { implicit: [true,true], value: 'block scalar with tag' }
+- !Scalar { value: 'data', style: '|', tag: '!mytag', implicit: [false,false] }
+- !Scalar { implicit: [true,true], value: 'single character' }
+- !Scalar { value: 'a', implicit: [true,true] }
+- !Scalar { implicit: [true,true], value: 'single digit' }
+- !Scalar { value: '1', implicit: [true,false] }
 - !MappingEnd
 - !DocumentEnd
 

tests/data/sequences.events

 - !DocumentEnd
 
 - !DocumentStart
-- !SequenceStart { tag: '!mytag' }
+- !SequenceStart { tag: '!mytag', implicit: false }
 - !SequenceEnd
 - !DocumentEnd
 
 - !SequenceStart
 - !SequenceStart
 - !SequenceEnd
-- !SequenceStart { tag: '!mytag' }
+- !SequenceStart { tag: '!mytag', implicit: false }
 - !SequenceEnd
 - !SequenceStart
 - !Scalar
 - !Scalar { value: 'data' }
-- !Scalar { tag: '!mytag', value: 'data' }
+- !Scalar { tag: '!mytag', implicit: [false,false], value: 'data' }
 - !SequenceEnd
 - !SequenceStart
 - !SequenceStart
 - !SequenceEnd
 - !SequenceEnd
 - !SequenceStart
-- !SequenceStart { tag: '!mytag' }
+- !SequenceStart { tag: '!mytag', implicit: false }
 - !SequenceStart
 - !Scalar { value: 'data' }
 - !SequenceEnd
 - !Scalar { value: 'data2' }
 - !SequenceEnd
 - !Scalar { value: 'key2' }
-- !SequenceStart { tag: '!mytag1' }
+- !SequenceStart { tag: '!mytag1', implicit: false }
 - !Scalar { value: 'data3' }
 - !SequenceStart
 - !Scalar { value: 'data4' }
 - !Scalar { value: 'data5' }
 - !SequenceEnd
-- !SequenceStart { tag: '!mytag2' }
+- !SequenceStart { tag: '!mytag2', implicit: false }
 - !Scalar { value: 'data6' }
 - !Scalar { value: 'data7' }
 - !SequenceEnd
 - !SequenceEnd
 - !Scalar
 - !Scalar { value: 'data' }
-- !Scalar { tag: '!mytag', value: 'data' }
-- !SequenceStart { tag: '!mytag' }
+- !Scalar { tag: '!mytag', implicit: [false,false], value: 'data' }
+- !SequenceStart { tag: '!mytag', implicit: false }
 - !Scalar { value: 'data' }
 - !Scalar { value: 'data' }
 - !SequenceEnd

tests/data/sloppy-indentation.canonical

 --- !!str
 "the parser does not require scalars to be indented with at least one space"
 --- !!map
-{ ? !!str "foo": { ? !!str "bar" : "quoted scalars may not adhere indentation" } }
+{ ? !!str "foo": { ? !!str "bar" : !!str "quoted scalars may not adhere indentation" } }

tests/test_appliance.py

             if self.check_token(TagToken):
                 tag = self.get_token_value()
             if self.check_token(ScalarToken):
-                self.events.append(ScalarEvent(anchor, tag, False, self.get_token_value(), None, None))
+                self.events.append(ScalarEvent(anchor, tag, (False, False), self.get_token_value(), None, None))
             elif self.check_token(FlowSequenceStartToken):
                 self.events.append(SequenceStartEvent(anchor, tag, None, None))
                 self.parse_sequence()
     def peek_event(self):
         return self.events[0]
 
-class CanonicalLoader(CanonicalScanner, CanonicalParser, Composer, Constructor, Detector):
+class CanonicalLoader(CanonicalScanner, CanonicalParser, Composer, Constructor, Resolver):
 
     def __init__(self, stream):
         if hasattr(stream, 'read'):
         CanonicalParser.__init__(self)
         Composer.__init__(self)
         Constructor.__init__(self)
-        Detector.__init__(self)
+        Resolver.__init__(self)
 
 def canonical_scan(stream):
     return scan(stream, Loader=CanonicalLoader)

tests/test_detector.py

-
-import test_appliance
-
-from yaml import *
-
-class TestDetector(test_appliance.TestAppliance):
-
-    def _testDetector(self, test_name, data_filename, detect_filename):
-        node = None
-        correct_tag = None
-        try:
-            correct_tag = file(detect_filename, 'rb').read().strip()
-            node = compose(file(data_filename, 'rb'))
-            self.failUnless(isinstance(node, SequenceNode))
-            for scalar in node.value:
-                self.failUnless(isinstance(scalar, ScalarNode))
-                self.failUnlessEqual(scalar.tag, correct_tag)
-        except:
-            print
-            print "DATA:"
-            print file(data_filename, 'rb').read()
-            print "CORRECT_TAG:"
-            print file(detect_filename, 'rb').read()
-            print "ROOT NODE:", node
-            print "SCALAR NODES:", node.value
-            raise
-
-TestDetector.add_tests('testDetector', '.data', '.detect')
-

tests/test_emitter.py

                 self.failUnlessEqual(event.tag, new_event.tag)
             if isinstance(event, ScalarEvent):
                 #self.failUnlessEqual(event.implicit, new_event.implicit)
-                if not event.implicit and not new_event.implicit:
+                if True not in event.implicit+new_event.implicit:
                     self.failUnlessEqual(event.tag, new_event.tag)
                 self.failUnlessEqual(event.value, new_event.value)
 
             mapping.setdefault('anchor', None)
         if class_name in ['ScalarEvent', 'SequenceStartEvent', 'MappingStartEvent']:
             mapping.setdefault('tag', None)
+        if class_name in ['SequenceStartEvent', 'MappingStartEvent']:
+            mapping.setdefault('implicit', True)
         if class_name == 'ScalarEvent':
-            mapping.setdefault('implicit', False)
+            mapping.setdefault('implicit', (False, True))
             mapping.setdefault('value', '')
         value = getattr(yaml, class_name)(**mapping)
         return value

tests/test_resolver.py

+
+import test_appliance
+
+from yaml import *
+
+class MyLoader(Loader):
+    pass
+
+class MyDumper(Dumper):
+    pass
+
+add_path_resolver(u'!root', [],
+        Loader=MyLoader, Dumper=MyDumper)
+
+add_path_resolver(u'!root/scalar', [], str,
+        Loader=MyLoader, Dumper=MyDumper)
+
+add_path_resolver(u'!root/key11/key12/*', ['key11', 'key12'],
+        Loader=MyLoader, Dumper=MyDumper)
+
+add_path_resolver(u'!root/key21/1/*', ['key21', 1],
+        Loader=MyLoader, Dumper=MyDumper)
+
+add_path_resolver(u'!root/key31/*/*/key14/map', ['key31', None, None, 'key14'], map,
+        Loader=MyLoader, Dumper=MyDumper)
+
+class TestResolver(test_appliance.TestAppliance):
+
+    def _testImplicitResolver(self, test_name, data_filename, detect_filename):
+        node = None
+        correct_tag = None
+        try:
+            correct_tag = file(detect_filename, 'rb').read().strip()
+            node = compose(file(data_filename, 'rb'))
+            self.failUnless(isinstance(node, SequenceNode))
+            for scalar in node.value:
+                self.failUnless(isinstance(scalar, ScalarNode))
+                self.failUnlessEqual(scalar.tag, correct_tag)
+        except:
+            print
+            print "DATA:"
+            print file(data_filename, 'rb').read()
+            print "CORRECT_TAG:"
+            print file(detect_filename, 'rb').read()
+            print "ROOT NODE:", node
+            print "SCALAR NODES:", node.value
+            raise
+
+    def _testPathResolverLoader(self, test_name, data_filename, path_filename):
+        #print serialize_all(compose_all(file(data_filename, 'rb').read(), Loader=MyLoader))
+        nodes1 = compose_all(file(data_filename, 'rb').read(), Loader=MyLoader)
+        nodes2 = compose_all(file(path_filename, 'rb').read())
+        for node1, node2 in zip(nodes1, nodes2):
+            self.failUnlessEqual(self._convert(node1), self._convert(node2))
+
+    def _testPathResolverDumper(self, test_name, data_filename, path_filename):
+        for filename in [data_filename, path_filename]:
+            output = serialize_all(compose_all(file(filename, 'rb').read()), Dumper=MyDumper)
+            #print output
+            nodes1 = compose_all(output)
+            nodes2 = compose_all(file(data_filename, 'rb').read())
+            for node1, node2 in zip(nodes1, nodes2):
+                self.failUnlessEqual(self._convert(node1), self._convert(node2))
+
+    def _convert(self, node):
+        if isinstance(node, ScalarNode):
+            return node.tag, node.value
+        elif isinstance(node, SequenceNode):
+            value = []
+            for item in node.value:
+                value.append(self._convert(item))
+            return node.tag, value
+        elif isinstance(node, MappingNode):
+            value = []
+            for key in node.value:
+                item = node.value[key]
+                value.append((self._convert(key), self._convert(item)))
+            value.sort()
+            return node.tag, value
+
+TestResolver.add_tests('testImplicitResolver', '.data', '.detect')
+TestResolver.add_tests('testPathResolverLoader', '.data', '.path')
+TestResolver.add_tests('testPathResolverDumper', '.data', '.path')
+

tests/test_yaml.py

 from test_tokens import *
 from test_structure import *
 from test_errors import *
-from test_detector import *
+from test_resolver import *
 from test_constructor import *
 from test_emitter import *
 from test_representer import *
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.