public DocumentRange Translate(TreeTextRange range)
        {
            if (!range.IsValid() || !SourceFile.IsValid())
            {
                return(DocumentRange.InvalidRange);
            }
            var sector = FindSectorAtRange(range);

            if (!sector.IsValid())
            {
                return(DocumentRange.InvalidRange);
            }

            // Let the included file handle the request
            if (sector.Include != null)
            {
                return(sector.Include.DocumentRangeTranslator.Translate(range));
            }

            // The range is in the current document

            // The includes that appear before do not contribute to document offset
            int extraIncludeOffset = sector.PrecedingIncludeLength;

            // Neither do the other includes in the tree
            // (the ones that are included before the current file)
            int extraRootOffset = FileLikeNode.GetTreeStartOffset().Offset;

            var start = range.StartOffset - extraIncludeOffset - extraRootOffset;
            var end   = range.EndOffset - extraIncludeOffset - extraRootOffset;
            var resultingTextRange = new TextRange(start.Offset, end.Offset);

            resultingTextRange.AssertValid();
            return(new DocumentRange(SourceFile.Document, resultingTextRange));
        }
        public TreeTextRange Translate(DocumentRange documentRange)
        {
            if (!documentRange.IsValid())
            {
                return(TreeTextRange.InvalidRange);
            }
            if (!SourceFile.IsValid())
            {
                return(TreeTextRange.InvalidRange);
            }
            if (!FileLikeNode.IsValid())
            {
                return(TreeTextRange.InvalidRange);
            }

            if (documentRange.Document != SourceFile.Document)
            {
                // That document might appear among the includes
                var rangeFromIncludes = Includes
                                        .Select(include => include.DocumentRangeTranslator.Translate(documentRange))
                                        .Where(textRange => textRange.IsValid())
                                        // Allow FirstOrDefault to return null
                                        .Select <TreeTextRange, TreeTextRange?>(it => it)
                                        .FirstOrDefault();
                return(rangeFromIncludes ?? TreeTextRange.InvalidRange);
            }

            // The range is in the same document as the source file we are responsible for,
            // so we have no choice but to handle the request ourselves
            (int documentStartOffset, int documentEndOffset) = documentRange.TextRange;
            var rootStartOffset = FileLikeNode.GetTreeStartOffset();

            // No includes, tree and document are matching
            if (!Includes.Any())
            {
                return(new TreeTextRange(rootStartOffset + documentStartOffset, rootStartOffset + documentEndOffset));
            }

            var treeStartOffset = Translate(documentStartOffset);

            if (!treeStartOffset.IsValid())
            {
                return(TreeTextRange.InvalidRange);
            }
            return(TreeTextRange.FromLength(treeStartOffset, documentRange.Length));
        }