Skip to content

Commit

Permalink
using legal interval tree implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
PankajBhojwani committed Sep 29, 2020
1 parent d1b2db3 commit 89b3c5a
Show file tree
Hide file tree
Showing 13 changed files with 514 additions and 185 deletions.
418 changes: 418 additions & 0 deletions oss/interval_tree/IntervalTree.h

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions oss/interval_tree/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2011 Erik Garrison

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
17 changes: 17 additions & 0 deletions oss/interval_tree/MAINTAINER_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### Notes for Future Maintainers

This was originally imported by @miniksa in January 2020.

The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
Please update the provenance information in that file when ingesting an updated version of the dependent library.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.

## What should be done to update this in the future?

1. Go to ekg/intervaltreerepository on GitHub.
2. Take the file IntervalTree.h wholesale and drop it into the directory here.
3. Don't change anything about it.
4. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in the same directory as this readme.
If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage.
5. Submit the pull.

13 changes: 13 additions & 0 deletions oss/interval_tree/cgmanifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{"Registrations":[
{
"component": {
"type": "git",
"git": {
"repositoryUrl": "https://github.com/ekg/intervaltree",
"commitHash": "b90527f9e6d51cd36ecbb50429e4524d3a418ea5"
}
}
}
],
"Version": 1
}
25 changes: 8 additions & 17 deletions src/buffer/out/textBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2375,15 +2375,11 @@ const size_t TextBuffer::AddPatternRecognizer(const std::string_view regexString
// - The firstRow to start searching from
// - The lastRow to search
// Return value:
// - A vector containing 3-element tuples where
// tuple<0>: ID of the pattern
// tuple<1>: start coordinate of the pattern
// tuple<2>: end coordinate of the pattern

til::IntervalTree::ITNode* TextBuffer::UpdatePatterns(const size_t firstRow, const size_t lastRow) const
// - An interval tree containing the patterns found
interval_tree::IntervalTree<size_t, size_t> TextBuffer::UpdatePatterns(const size_t firstRow, const size_t lastRow) const
{
til::IntervalTree tree;
til::IntervalTree::ITNode* result = NULL;
typedef interval_tree::IntervalTree<size_t, size_t> ITree;
ITree::interval_vector intervals;

std::wstring concatAll;
const auto rowSize = GetRowByOffset(0).size();
Expand Down Expand Up @@ -2421,18 +2417,13 @@ til::IntervalTree::ITNode* TextBuffer::UpdatePatterns(const size_t firstRow, con
const auto end = start + i->str().size();
lenUpToThis = end;

// store the locations as (col, row) coordinates
// NOTE: these are VIEWPORT coordinates, not buffer coordinates
// store the intervals
// NOTE: these intervals are relative to the VIEWPORT not the buffer
// Keeping these as viewport coordinates for now because its the renderer
// that actually uses these coordinates and the renderer works in viewport coords
const auto startRow = gsl::narrow<SHORT>(start / rowSize);
const auto startCol = gsl::narrow<SHORT>(start % rowSize);
const auto endRow = gsl::narrow<SHORT>(end / rowSize);
const auto endCol = gsl::narrow<SHORT>(end % rowSize);
const COORD startCoord{ startCol, startRow };
const COORD endCoord{ endCol, endRow };
result = tree.insert(result, til::IntervalTree::Interval{ startCoord, endCoord }, idAndPattern.first);
intervals.push_back(ITree::interval(start, end, idAndPattern.first));
}
}
ITree result(std::move(intervals));
return result;
}
2 changes: 1 addition & 1 deletion src/buffer/out/textBuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class TextBuffer final
std::optional<std::reference_wrapper<PositionInformation>> positionInfo);

const size_t AddPatternRecognizer(const std::string_view regexString);
til::IntervalTree::ITNode* UpdatePatterns(const size_t firstRow, const size_t lastRow) const;
interval_tree::IntervalTree<size_t, size_t> UpdatePatterns(const size_t firstRow, const size_t lastRow) const;

private:
void _UpdateSize();
Expand Down
34 changes: 22 additions & 12 deletions src/cascadia/TerminalCore/Terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,20 +424,30 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position)
return uri;
}
// also look through our known pattern locations
const auto found = _tree.overlapSearch(_patternsAndLocations, til::IntervalTree::Interval{ position, position });
if (found != NULL && found->patternId == _hyperlinkPatternId)
const auto absLoc = (_buffer->GetRowByOffset(0).size() * position.Y) + position.X;
const auto results = _patternIntervalTree.findOverlapping(absLoc + 1, absLoc);
if (results.size() > 0)
{
const auto start = found->i->low;
const auto end = found->i->high;
std::wstring uri;

const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start));
const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end));
for (auto iter = startIter; iter != endIter; ++iter)
const auto rowSize = _buffer->GetRowByOffset(0).size();
for (auto result : results)
{
uri += iter->Chars();
if (result.value == _hyperlinkPatternId)
{
const auto start = results.at(0).start;
const auto end = results.at(0).stop;
COORD startCoord{ gsl::narrow<SHORT>(start % rowSize), gsl::narrow<SHORT>(start / rowSize) };
COORD endCoord{ gsl::narrow<SHORT>(end % rowSize), gsl::narrow<SHORT>(end / rowSize) };
std::wstring uri;

const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(startCoord));
const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(endCoord));
for (auto iter = startIter; iter != endIter; ++iter)
{
uri += iter->Chars();
}
return uri;
}
}
return uri;
}
return {};
}
Expand Down Expand Up @@ -1057,7 +1067,7 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept
// region changes (for example by text entering the buffer or scrolling)
void Microsoft::Terminal::Core::Terminal::UpdatePatterns() noexcept
{
_patternsAndLocations = _buffer->UpdatePatterns(_VisibleStartIndex(), _VisibleEndIndex());
_patternIntervalTree = _buffer->UpdatePatterns(_VisibleStartIndex(), _VisibleEndIndex());
}

const std::optional<til::color> Terminal::GetTabColor() const noexcept
Expand Down
4 changes: 2 additions & 2 deletions src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,6 @@ class Microsoft::Terminal::Core::Terminal final :
bool _suppressApplicationTitle;

size_t _hyperlinkPatternId;
til::IntervalTree _tree;
til::IntervalTree::ITNode* _patternsAndLocations;

#pragma region Text Selection
// a selection is represented as a range between two COORDs (start and end)
Expand Down Expand Up @@ -277,6 +275,8 @@ class Microsoft::Terminal::Core::Terminal final :
// underneath them, while others would prefer to anchor it in place.
// Either way, we should make this behavior controlled by a setting.

interval_tree::IntervalTree<size_t, size_t> _patternIntervalTree;

// Since virtual keys are non-zero, you assume that this field is empty/invalid if it is.
struct KeyEventCodes
{
Expand Down
14 changes: 10 additions & 4 deletions src/cascadia/TerminalCore/terminalrenderdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,18 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin
// - The pattern ID of the location
const size_t Microsoft::Terminal::Core::Terminal::GetPatternId(const COORD location) const noexcept
{
const auto found = _tree.overlapSearch(_patternsAndLocations, til::IntervalTree::Interval{ location, location });
if (found != NULL)
// Convert the location into its 1-d location because the interval tree stores locations that way
const auto absLoc = (_buffer->GetRowByOffset(0).size() * location.Y) + location.X;
const auto results = _patternIntervalTree.findOverlapping(absLoc + 1, absLoc);
if (results.size() == 0)
{
return found->patternId;
return 0;
}
else
{
// At some point, this should be updated to return a vector of IDs
return results.at(0).value;
}
return 0;
}

std::vector<Microsoft::Console::Types::Viewport> Terminal::GetSelectionRects() noexcept
Expand Down
2 changes: 1 addition & 1 deletion src/common.build.pre.props
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
Expand Down
3 changes: 3 additions & 0 deletions src/inc/LibraryIncludes.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@
// {fmt}, a C++20-compatible formatting library
#include <fmt/format.h>

#define USE_INTERVAL_TREE_NAMESPACE
#include <IntervalTree.h>

// SAL
#include <sal.h>

Expand Down
1 change: 0 additions & 1 deletion src/inc/til.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "til/spsc.h"
#include "til/coalesce.h"
#include "til/replace.h"
#include "til/interval_tree.h"

namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
Expand Down
147 changes: 0 additions & 147 deletions src/inc/til/interval_tree.h

This file was deleted.

1 comment on commit 89b3c5a

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New misspellings found, please review:

  • ITree
To accept these changes, run the following commands
perl -e '
my @expect_files=qw('".github/actions/spell-check/expect/alphabet.txt
.github/actions/spell-check/expect/expect.txt
.github/actions/spell-check/expect/web.txt"');
@ARGV=@expect_files;
my @stale=qw('"Autogenerated debugbreak DECLL DECSMBV Inplace notypeopt restrictederrorinfo Scs Switchto Wlk "');
my $re=join "|", @stale;
my $suffix=".".time();
my $previous="";
sub maybe_unlink { unlink($_[0]) if $_[0]; }
while (<>) {
  if ($ARGV ne $old_argv) { maybe_unlink($previous); $previous="$ARGV$suffix"; rename($ARGV, $previous); open(ARGV_OUT, ">$ARGV"); select(ARGV_OUT); $old_argv = $ARGV; }
  next if /^($re)(?:$| .*)/; print;
}; maybe_unlink($previous);'
perl -e '
my $new_expect_file=".github/actions/spell-check/expect/89b3c5a23818bf0c108e5d2632a08d95f024e235.txt";
open FILE, q{<}, $new_expect_file; chomp(my @words = <FILE>); close FILE;
my @add=qw('"autogenerated inplace ITree "');
my %items; @items{@words} = @words x (1); @items{@add} = @add x (1);
@words = sort {lc($a) cmp lc($b)} keys %items;
open FILE, q{>}, $new_expect_file; for my $word (@words) { print FILE "$word\n" if $word =~ /\w/; };
close FILE;'
git add .github/actions/spell-check/expect || echo '... you want to ensure .github/actions/spell-check/expect/89b3c5a23818bf0c108e5d2632a08d95f024e235.txt is added to your repository...'
✏️ Contributor please read this

By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.

⚠️ The command is written for posix shells. You can copy the contents of each perl command excluding the outer ' marks and dropping any '"/"' quotation mark pairs into a file and then run perl file.pl from the root of the repository to run the code. Alternatively, you can manually insert the items...

If the listed items are:

  • ... misspelled, then please correct them instead of using the command.
  • ... names, please add them to .github/actions/spell-check/dictionary/names.txt.
  • ... APIs, you can add them to a file in .github/actions/spell-check/dictionary/.
  • ... just things you're using, please add them to an appropriate file in .github/actions/spell-check/expect/.
  • ... tokens you only need in one place and shouldn't generally be used, you can add an item in an appropriate file in .github/actions/spell-check/patterns/.

See the README.md in each directory for more information.

🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The :check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉

⚠️ Reviewers

At present, the action that triggered this message will not show its ❌ in this PR unless the branch is within this repository.
Thus, you should make sure that this comment has been addressed before encouraging the merge bot to merge this PR.

Please sign in to comment.