public static void Encode(StringBuilder sb, int value) { int vlq = VLQ.ToVLQSigned(value); do { int digit = (vlq & VLQ.BaseMask); vlq >>= VLQ.BaseShift; if (vlq > 0) { digit |= VLQ.ContinuationBit; } sb.Append(VLQ.Base64Chars[digit]); }while (vlq > 0); }
public void Write(Stream stream, string generatedFilePath, string sourceFilePathRoot) { if (string.IsNullOrEmpty(sourceFilePathRoot) == false && (sourceFilePathRoot.EndsWith("\\") || sourceFilePathRoot.EndsWith("/")) == false) { sourceFilePathRoot = sourceFilePathRoot + Path.DirectorySeparatorChar; } // Gather sources var sources = new List <string>(); var sourcesIndex = new Dictionary <string, int>(); foreach (var mappingEntry in Mappings) { string filepath = AbsolutePath(mappingEntry.Source.Name); if (sourcesIndex.ContainsKey(filepath)) { continue; } sourcesIndex.Add(filepath, sources.Count); string relativeFilePath = mappingEntry.Source.Name; if (string.IsNullOrEmpty(sourceFilePathRoot) == false) { var rootUri = new UriBuilder(sourceFilePathRoot).Uri; //Note(Maik): Path.MakeRelativeUri() requires the presence of a trailing slash in the // 'this' URI. relativeFilePath = rootUri.MakeRelativeUri(new UriBuilder(mappingEntry.Source.Name).Uri).ToString(); } sources.Add(relativeFilePath); } // Build mappings var mappings = new StringBuilder(); var indices = new StringBuilder(); var columnInterpolation = new StringBuilder(); bool newLine = true; int prevSourceLine = 1; int prevSourceColumn = 1; int prevGeneratedLine = 1; int prevGeneratedColumn = 1; int prevSourcesIndex = 0; int prevCallerIndex = 0; foreach (var mappingEntry in Mappings) { int lineDiff = (mappingEntry.Generated.Line - prevGeneratedLine); for (int i = 0; i < lineDiff; ++i) { mappings.Append(";"); indices.Append(";"); columnInterpolation.Append(";"); } if (lineDiff > 0) { prevGeneratedLine = mappingEntry.Generated.Line; prevGeneratedColumn = 1; newLine = true; } if (lineDiff == 0 && newLine == false) { mappings.Append(","); indices.Append(","); columnInterpolation.Append(","); } // Field 1: "zero-based starting column of the line in the generated code" int generatedColumn = mappingEntry.Generated.Column; VLQ.Encode(mappings, generatedColumn - prevGeneratedColumn); prevGeneratedColumn = generatedColumn; // Field 2: "zero-based index into the sources list" int sourceIndex = sourcesIndex[AbsolutePath(mappingEntry.Source.Name)]; VLQ.Encode(mappings, sourceIndex - prevSourcesIndex); prevSourcesIndex = sourceIndex; // Field 3: "zero-based starting line in the source" int sourceLine = mappingEntry.Source.Line; VLQ.Encode(mappings, sourceLine - prevSourceLine); prevSourceLine = sourceLine; // Field 4: "zero-based starting column of the line in the source" int sourceColumn = mappingEntry.Source.Column; VLQ.Encode(mappings, sourceColumn - prevSourceColumn); prevSourceColumn = sourceColumn; // // int callerIndex = mappingEntry.CallerIndex; if (callerIndex != -1) { VLQ.Encode(indices, callerIndex - prevCallerIndex); prevCallerIndex = callerIndex; } // // if ((mappingEntry.Features & EntryFeatures.ColumnInterpolation) != 0) { VLQ.Encode(columnInterpolation, 1); } newLine = false; } // Build Callstack var callers = new StringBuilder(); prevSourcesIndex = 0; prevSourceLine = 1; prevSourceColumn = 1; int prevParentIndex = 0; foreach (var caller in Callers) { if (callers.Length > 0) { callers.Append(";"); } // Field 1: "zero-based index into the sources list" int sourceIndex = sourcesIndex[AbsolutePath(caller.Source.Name)]; VLQ.Encode(callers, sourceIndex - prevSourcesIndex); prevSourcesIndex = sourceIndex; // Field 2: "zero-based starting line in the source" int sourceLine = caller.Source.Line; VLQ.Encode(callers, sourceLine - prevSourceLine); prevSourceLine = sourceLine; // Field 3: "zero-based starting column of the line in the source" int sourceColumn = caller.Source.Column; VLQ.Encode(callers, sourceColumn - prevSourceColumn); prevSourceColumn = sourceColumn; // Field 4: "zero-based index of parent caller" int parentIndex = caller.ParentIndex; if (parentIndex != -1) { VLQ.Encode(callers, parentIndex - prevParentIndex); prevParentIndex = parentIndex; } } var callstack = new CallstackExtension { Callers = callers.ToString(), Indices = indices.ToString() }; var graph = new SourceMapGraph { File = new UriBuilder(generatedFilePath).Path, SourceRoot = string.IsNullOrEmpty(sourceFilePathRoot) ? string.Empty : new UriBuilder(sourceFilePathRoot).Path, Sources = sources, Mappings = mappings.ToString(), Callstack = callstack, ColumnInterpolation = columnInterpolation.ToString() }; var serializer = new DataContractJsonSerializer(typeof(SourceMapGraph)); serializer.WriteObject(stream, graph); }