// Copyright Epic Games, Inc. All Rights Reserved.
using IncludeTool.Support;
using EpicGames.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IncludeTool
{
///
/// Represents an fragment of a source file consisting of a sequence of includes followed by some arbitrary directives or source code.
///
[Serializable]
class SourceFragment
{
///
/// The file that this fragment is part of
///
public SourceFile File;
///
/// Index into the file's markup array of the start of this fragment (inclusive)
///
public int MarkupMin;
///
/// Index into the file's markup array of the end of this fragment (exclusive). Set to zero for external files.
///
public int MarkupMax;
///
/// Unique name assigned to this fragment.
///
public string UniqueName;
///
/// Location of this fragment on disk. May be the original file in the case the single fragment for an external file, or a written out extract corresponding to the markup range.
///
public FileReference Location;
///
/// Set of fragments which this fragment is dependent on
///
public HashSet Dependencies;
///
/// All the imported symbols for this fragment
///
public Dictionary ReferencedSymbols;
///
/// SHA1 digest of this fragment's text on disk
///
public string Digest;
///
/// Constructor
///
/// The source file that this fragment is part of
/// Index into the file's markup array of the start of this fragment (inclusive)
/// Index into the file's markup array of the end of this fragment (exclusive)
public SourceFragment(SourceFile File, int MarkupMin, int MarkupMax)
{
this.File = File;
this.MarkupMin = MarkupMin;
this.MarkupMax = MarkupMax;
}
///
/// Find a flat list of fragments included by this file
///
/// List of included fragments, in order
public List FindIncludedFragments()
{
List IncludedFragments = new List();
FindIncludedFragments(IncludedFragments, null, new HashSet());
return IncludedFragments;
}
///
/// Find all the fragments included by this fragment, including the current one
///
/// List to receive the included fragments
/// Optional list to store changes to the include stack, and the count of fragments at that point. Records with a file set to null indicate that the end of the topmost file.
/// Set of files with header guards that have already been included
public void FindIncludedFragments(List IncludedFragments, List> IncludeStackChanges, HashSet UniqueIncludedFiles)
{
if((File.Flags & SourceFileFlags.Inline) == 0)
{
// Add all the included files into the tree, and save the depth at each step
for(int MarkupIdx = MarkupMin; MarkupIdx < MarkupMax; MarkupIdx++)
{
PreprocessorMarkup Item = File.Markup[MarkupIdx];
if(Item.Type == PreprocessorMarkupType.Include && Item.IsActive && Item.IncludedFile != null)
{
SourceFile IncludedFile = Item.IncludedFile;
if(!IncludedFile.HasHeaderGuard || UniqueIncludedFiles.Add(IncludedFile))
{
IncludedFile.FindIncludedFragments(IncludedFragments, IncludeStackChanges, UniqueIncludedFiles);
}
}
}
// Add the current fragment
IncludedFragments.Add(this);
}
}
///
/// The text location of the start of this fragment
///
public TextLocation MinLocation
{
get { return (MarkupMin < File.Markup.Length)? File.Markup[MarkupMin].Location : File.Text.End; }
}
///
/// The text location of the end of this fragment
///
public TextLocation MaxLocation
{
get { return (MarkupMax < File.Markup.Length)? File.Markup[MarkupMax].Location : File.Text.End; }
}
///
/// Writes this fragment to a file
///
/// Writer to receive output text
public void Write(TextWriter Writer)
{
TextBuffer Text = File.Text;
// Write all the relevant bits from this file
TextLocation Location = TextLocation.Origin;
for(int Idx = File.BodyMinIdx; Idx < File.BodyMaxIdx; Idx++)
{
PreprocessorMarkup Markup = File.Markup[Idx];
if((Idx >= MarkupMin && Idx < MarkupMax) || Markup.IsConditionalPreprocessorDirective())
{
if(Markup.Type != PreprocessorMarkupType.Include)
{
TextLocation EndLocation = (Idx + 1 < File.Markup.Length)? File.Markup[Idx + 1].Location : File.Text.End;
WriteLinesCommented(Writer, Text, Location, Markup.Location);
WriteLines(Writer, Text, Markup.Location, EndLocation);
Location = EndLocation;
}
else if(!Markup.IsActive)
{
WriteLinesCommented(Writer, Text, Location, Markup.Location);
Writer.WriteLine("#error Unexpected include: {0}", Token.Format(Markup.Tokens));
Location = new TextLocation(Markup.Location.LineIdx + 1, 0);
}
else if(Markup.IncludedFile != null && (Markup.IncludedFile.Flags & SourceFileFlags.Inline) != 0)
{
TextLocation EndLocation = (Idx + 1 < File.Markup.Length)? File.Markup[Idx + 1].Location : File.Text.End;
WriteLinesCommented(Writer, Text, Location, Markup.Location);
Writer.Write("#include \"{0}\"", Markup.IncludedFile.Location.FullName);
WriteLinesCommented(Writer, Text, Markup.Location, EndLocation);
Location = EndLocation;
}
}
}
// Comment out the rest of the file
WriteLinesCommented(Writer, Text, Location, Text.End);
}
///
/// Utility function to write out a portion of a TextBuffer to a TextWriter
///
/// The writer to receive the extracted text
/// The text buffer being read from
/// Start location of the text to extract
/// End location of the text to extract
static void WriteLines(TextWriter Writer, TextBuffer Text, TextLocation Location, TextLocation EndLocation)
{
string[] TextLines = Text.Extract(Location, EndLocation);
for(int Idx = 0; Idx < TextLines.Length - 1; Idx++)
{
Writer.WriteLine(TextLines[Idx]);
}
Writer.Write(TextLines[TextLines.Length - 1]);
}
///
/// Utility function to write out a portion of a TextBuffer to a TextWriter as comments
///
/// The writer to receive the extracted text
/// The text buffer being read from
/// Start location of the text to extract
/// End location of the text to extract
static void WriteLinesCommented(TextWriter Writer, TextBuffer Text, TextLocation Location, TextLocation EndLocation)
{
string[] CommentLines = Text.Extract(Location, EndLocation);
for(int Idx = 0; Idx < CommentLines.Length - 1; Idx++)
{
string CommentLine = CommentLines[Idx];
if(CommentLine.EndsWith("\\")) CommentLine = CommentLine.Substring(CommentLine.Length - 1);
Writer.WriteLine("// XXX {0}", CommentLine);
}
}
///
/// Summarize this fragment for the debugger
///
/// String representation of this fragment for the debugger
public override string ToString()
{
if(Location != null)
{
return Location.ToString();
}
else if(MarkupMax == 0)
{
return File.ToString();
}
else
{
return String.Format("{0}: {1}-{2}", File.ToString(), File.Markup[MarkupMin].Location.LineIdx + 1, File.Markup[MarkupMax - 1].EndLocation.LineIdx + 1);
}
}
}
}