Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving multiline context snippet #2288

Merged
merged 6 commits into from
Feb 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions src/Sarif/FileRegionsCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private Region PopulateTextRegionProperties(NewLineIndex lineIndex, Region input

internal Region ConstructMultilineContextSnippet(Region inputRegion, Uri uri)
{
if (inputRegion == null || inputRegion.IsBinaryRegion)
if (inputRegion?.IsBinaryRegion != false)
{
// Context snippets are relevant only for textual regions.
return null;
Expand All @@ -156,16 +156,44 @@ internal Region ConstructMultilineContextSnippet(Region inputRegion, Uri uri)
return null;
}

const int bigSnippetLength = 512;
const int smallSnippetLength = 128;

// Generating full inputRegion to prevent issues.
Region originalRegion = this.PopulateTextRegionProperties(inputRegion, uri, populateSnippet: true);

int maxLineNumber = newLineIndex.MaximumLineNumber;

// Currently, we just grab a single line before and after the region start
// and end lines, respectively. In the future, we could make this configurable.
var region = new Region()
var region = new Region
{
StartLine = inputRegion.StartLine == 1 ? 1 : inputRegion.StartLine - 1,
EndLine = inputRegion.EndLine == maxLineNumber ? maxLineNumber : inputRegion.EndLine + 1
};
return this.PopulateTextRegionProperties(region, uri, populateSnippet: true);

// Generating multilineRegion with one line before and after.
Region multilineContextSnippet = this.PopulateTextRegionProperties(region, uri, populateSnippet: true);

if (originalRegion.CharLength <= multilineContextSnippet.CharLength &&
multilineContextSnippet.CharLength <= bigSnippetLength)
{
return multilineContextSnippet;
}

region.CharOffset = originalRegion.CharOffset < smallSnippetLength
? 0
: originalRegion.CharOffset - smallSnippetLength;

region.CharLength = originalRegion.CharLength + originalRegion.CharOffset + smallSnippetLength < newLineIndex.Text.Length
? originalRegion.CharLength + originalRegion.CharOffset + smallSnippetLength
: newLineIndex.Text.Length - region.CharOffset;

// Generating multineRegion with 128 characters to the left and right from the
// originalRegion if possible.
multilineContextSnippet = this.PopulateTextRegionProperties(region, uri, populateSnippet: true);

// We can't generate a contextRegion which is smaller than the original region.
Debug.Assert(originalRegion.CharLength <= multilineContextSnippet.CharLength);
return multilineContextSnippet;
}

private void PopulatePropertiesFromCharOffsetAndLength(NewLineIndex newLineIndex, Region region)
Expand Down
63 changes: 63 additions & 0 deletions src/Test.UnitTests.Sarif/FileRegionsCacheTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -583,5 +583,68 @@ public void FileRegionsCache_PopulatesNullRegion()
region = fileRegionsCache.PopulateTextRegionProperties(inputRegion: null, uri: uri, populateSnippet: true);
region.Should().BeNull();
}

[Fact]
public void FileRegionsCache_IncreasingToLeftAndRight()
{
Uri uri = new Uri(@"c:\temp\myFile.cpp");
string fileContent = $"{new string('a', 200)}{new string('b', 800)}";

var region = new Region
{
CharOffset = 114,
CharLength = 600,
};

var fileRegionsCache = new FileRegionsCache();
region = fileRegionsCache.PopulateTextRegionProperties(region, uri, true, fileContent);

Region multilineRegion = fileRegionsCache.ConstructMultilineContextSnippet(region, uri);

// 114 (charoffset) + 600 (charlength) + 128 (grabbing right content)
multilineRegion.CharLength.Should().Be(114 + 600 + 128);
}

[Fact]
public void FileRegionsCache_PopulatesWithOneLine_IncreasingToTheRight()
{
string content = $"{new string('a', 200)}{new string('b', 800)}";
var uri = new Uri(@"c:\temp\myFile.cpp");
var region = new Region
{
CharOffset = 0,
CharLength = 300,
};

var fileRegionsCache = new FileRegionsCache();
region = fileRegionsCache.PopulateTextRegionProperties(region, uri, true, content);
Region multilineRegion = fileRegionsCache.ConstructMultilineContextSnippet(region, uri);

// CharLength + 128 to the right = 428 characters
// 200 letters a + 228 letters b
multilineRegion.CharLength.Should().Be(300 + 128);
multilineRegion.Snippet.Text.Should().Be($"{new string('a', 200)}{new string('b', 228)}");
}

[Fact]
public void FileRegionsCache_PopulatesWithOneLine_Everything()
{
string content = $"{new string('a', 200)}{new string('b', 200)}";
var uri = new Uri(@"c:\temp\myFile.cpp");
var region = new Region
{
CharOffset = 0,
CharLength = 300,
};

var fileRegionsCache = new FileRegionsCache();
region = fileRegionsCache.PopulateTextRegionProperties(region, uri, true, content);
Region multilineRegion = fileRegionsCache.ConstructMultilineContextSnippet(region, uri);

// Since the content is 400, the charLength will be 400
// and the snippet.Text will be the entire content.
multilineRegion.CharLength.Should().Be(content.Length);
multilineRegion.Snippet.Text.Should().Be(content);
}
}
}