diff --git a/.gitignore b/.gitignore index 2da1447..96dbd90 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -.clang_complete node_modules -dist -.DS_Store +npm-debug.log* build -debug.html + +*.gypi +!/extra.gypi diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..5d1474d --- /dev/null +++ b/.npmignore @@ -0,0 +1,7 @@ +node_modules + +/build +!/build/Release/nbind.js +!/build/Release/nbind.js.mem + +*.gypi diff --git a/.travis.yml b/.travis.yml index cb0868f..5652f0b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -sudo: false +sudo: required git: depth: 10 @@ -7,12 +7,22 @@ language: node_js node_js: 6 -env: - - CC=clang CXX=clang++ npm_config_clang=1 - -script: npm test +script: npm run test:noasmjs notifications: email: on_success: never on_failure: change + +env: + - CXX=clang++-3.8 + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + packages: + - gcc-5 + - g++-5 + - clang-3.8 diff --git a/README.md b/README.md new file mode 100644 index 0000000..90c7a96 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# buffer-offset-index diff --git a/autogypi.json b/autogypi.json new file mode 100644 index 0000000..a62cdbd --- /dev/null +++ b/autogypi.json @@ -0,0 +1,6 @@ +{ + "dependencies": [ + "nbind" + ], + "includes": [] +} diff --git a/binding.gyp b/binding.gyp index 9f60f72..3ed4e42 100644 --- a/binding.gyp +++ b/binding.gyp @@ -1,26 +1,19 @@ { - "targets": [ - { - "target_name": "buffer_offset_index", - "cflags_cc": ["-std=c++11"], - "sources": [ - "src/binding.cc", - "src/buffer_offset_index.cc" - ], - "include_dirs": [ - "src", - '&1 > /dev/null || npm run build:browser", + "prepublish": "[ \"$CI\" = true ] || npm run build:browser" }, "author": "", "license": "MIT", - "gypfile": true, "devDependencies": { "mocha": "^3.1.2", "random-seed": "^0.3.0", "segfault-handler": "^1.0.0" }, "dependencies": { - "nan": "^2.4.0" + "autogypi": "^0.2.2", + "nbind": "^0.3.5", + "node-gyp": "^3.4.0" } } diff --git a/src/binding.cc b/src/binding.cc deleted file mode 100644 index 7b63734..0000000 --- a/src/binding.cc +++ /dev/null @@ -1,154 +0,0 @@ -#include "nan.h" -#include "buffer_offset_index.h" -#include "point.h" - -using namespace v8; - -static Nan::Persistent row_string; -static Nan::Persistent column_string; - -static Nan::Maybe PointFromJS(Nan::MaybeLocal maybe_object) { - Local object; - if (!maybe_object.ToLocal(&object)) { - Nan::ThrowTypeError("Expected an object with 'row' and 'column' properties."); - return Nan::Nothing(); - } - - Nan::MaybeLocal maybe_row = Nan::To(object->Get(Nan::New(row_string))); - Local js_row; - if (!maybe_row.ToLocal(&js_row)) { - Nan::ThrowTypeError("Expected an object with 'row' and 'column' properties."); - return Nan::Nothing(); - } - - Nan::MaybeLocal maybe_column = Nan::To(object->Get(Nan::New(column_string))); - Local js_column; - if (!maybe_column.ToLocal(&js_column)) { - Nan::ThrowTypeError("Expected an object with 'row' and 'column' properties."); - return Nan::Nothing(); - } - - unsigned row, column; - if (std::isfinite(js_row->NumberValue())) { - row = static_cast(js_row->Int32Value()); - } else { - row = UINT_MAX; - } - - if (std::isfinite(js_column->NumberValue())) { - column = static_cast(js_column->Int32Value()); - } else { - column = UINT_MAX; - } - - return Nan::Just(Point {row, column}); -} - -class PointWrapper : public Nan::ObjectWrap { -public: - static void Init() { - Local constructor_template = Nan::New(New); - constructor_template->SetClassName(Nan::New("Point").ToLocalChecked()); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - Nan::SetAccessor(constructor_template->InstanceTemplate(), Nan::New(row_string), GetRow); - Nan::SetAccessor(constructor_template->InstanceTemplate(), Nan::New(column_string), GetColumn); - constructor.Reset(constructor_template->GetFunction()); - } - - static Local FromPoint(Point point) { - Local result; - if (Nan::New(constructor)->NewInstance(Nan::GetCurrentContext()).ToLocal(&result)) { - (new PointWrapper(point))->Wrap(result); - return result; - } else { - return Nan::Null(); - } - } - -private: - PointWrapper(Point p): point(p) {} - - static void New(const Nan::FunctionCallbackInfo &info) {} - - static void GetRow(v8::Local property, const Nan::PropertyCallbackInfo& info) { - PointWrapper *wrapper = Nan::ObjectWrap::Unwrap(info.This()); - Point &point = wrapper->point; - info.GetReturnValue().Set(Nan::New(point.row)); - } - - static void GetColumn(v8::Local property, const Nan::PropertyCallbackInfo& info) { - PointWrapper *wrapper = Nan::ObjectWrap::Unwrap(info.This()); - Point &point = wrapper->point; - info.GetReturnValue().Set(Nan::New(point.column)); - } - - static Nan::Persistent constructor; - Point point; -}; - -Nan::Persistent PointWrapper::constructor; - -class BufferOffsetIndexWrapper : public Nan::ObjectWrap { -public: - static void Init(Local exports, Local module) { - Local constructor_template = Nan::New(New); - constructor_template->SetClassName(Nan::New("BufferOffsetIndex").ToLocalChecked()); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - const auto &prototype_template = constructor_template->PrototypeTemplate(); - prototype_template->Set(Nan::New("splice").ToLocalChecked(), Nan::New(Splice)); - prototype_template->Set(Nan::New("positionForCharacterIndex").ToLocalChecked(), Nan::New(PositionForCharacterIndex)); - prototype_template->Set(Nan::New("characterIndexForPosition").ToLocalChecked(), Nan::New(CharacterIndexForPosition)); - module->Set(Nan::New("exports").ToLocalChecked(), constructor_template->GetFunction()); - } - -private: - static void New(const Nan::FunctionCallbackInfo &info) { - BufferOffsetIndexWrapper *buffer_offset_index = new BufferOffsetIndexWrapper(); - buffer_offset_index->Wrap(info.This()); - } - - static void Splice(const Nan::FunctionCallbackInfo &info) { - BufferOffsetIndex &buffer_offset_index = Nan::ObjectWrap::Unwrap(info.This())->buffer_offset_index; - - auto start_row = info[0].As()->Value(); - auto deleted_lines_count = info[1].As()->Value(); - auto js_new_line_lengths = info[2].As(); - auto new_line_lengths = std::vector(js_new_line_lengths->Length()); - for (size_t i = 0; i < new_line_lengths.size(); i++) { - new_line_lengths[i] = js_new_line_lengths->Get(i).As()->Value(); - } - buffer_offset_index.Splice(start_row, deleted_lines_count, new_line_lengths); - } - - static void CharacterIndexForPosition(const Nan::FunctionCallbackInfo &info) { - BufferOffsetIndex &buffer_offset_index = Nan::ObjectWrap::Unwrap(info.This())->buffer_offset_index; - - auto position = PointFromJS(Nan::To(info[0])); - if (position.IsJust()) { - auto result = buffer_offset_index.CharacterIndexForPosition(position.FromJust()); - info.GetReturnValue().Set(Nan::New(result)); - } - } - - static void PositionForCharacterIndex(const Nan::FunctionCallbackInfo &info) { - BufferOffsetIndex &buffer_offset_index = Nan::ObjectWrap::Unwrap(info.This())->buffer_offset_index; - - auto character_index = Nan::To(info[0]); - if (character_index.IsJust()) { - auto result = buffer_offset_index.PositionForCharacterIndex(character_index.FromJust()); - info.GetReturnValue().Set(PointWrapper::FromPoint(result)); - } - } - - BufferOffsetIndex buffer_offset_index; -}; - -void Init(Local exports, Local module) { - row_string.Reset(Nan::Persistent(Nan::New("row").ToLocalChecked())); - column_string.Reset(Nan::Persistent(Nan::New("column").ToLocalChecked())); - - PointWrapper::Init(); - BufferOffsetIndexWrapper::Init(exports, module); -} - -NODE_MODULE(atom_buffer_offset_index, Init) diff --git a/src/bindings.cc b/src/bindings.cc new file mode 100644 index 0000000..6cfeda9 --- /dev/null +++ b/src/bindings.cc @@ -0,0 +1,26 @@ +#include "point.h" + +#include "buffer_offset_index.h" +#include "buffer_offset_index.cc" + +#include "nbind/nbind.h" + +NBIND_CLASS(Point) { + + construct<>(); + construct(); + + getset(getRow, setRow); + getset(getColumn, setColumn); + +} + +NBIND_CLASS(BufferOffsetIndex) { + + construct<>(); + + method(splice); + method(characterIndexForPosition); + method(positionForCharacterIndex); + +} diff --git a/src/buffer_offset_index.cc b/src/buffer_offset_index.cc index e9c1ac2..6c6ab7b 100644 --- a/src/buffer_offset_index.cc +++ b/src/buffer_offset_index.cc @@ -24,7 +24,7 @@ struct LineNode { } } - void compute_subtree_extents() { + void computeSubtreeExtents() { if (left) { left_subtree_extent = Extent { left->left_subtree_extent.characters + left->length + left->right_subtree_extent.characters, @@ -53,9 +53,9 @@ BufferOffsetIndex::~BufferOffsetIndex() { } } -void BufferOffsetIndex::Splice(unsigned start_row, unsigned deleted_lines_count, std::vector &new_line_lengths) { - auto start_node = FindAndBubbleNodeUpToRoot(start_row - 1); - auto end_node = FindAndBubbleNodeUpToRoot(start_row + deleted_lines_count); +void BufferOffsetIndex::splice(unsigned start_row, unsigned deleted_lines_count, std::vector new_line_lengths) { + auto start_node = findAndBubbleNodeUpToRoot(start_row - 1); + auto end_node = findAndBubbleNodeUpToRoot(start_row + deleted_lines_count); if (start_node) { if (start_node->right) { @@ -64,10 +64,10 @@ void BufferOffsetIndex::Splice(unsigned start_row, unsigned deleted_lines_count, } if (!new_line_lengths.empty()) { - start_node->right = BuildLineNodeTreeFromLineLengths(new_line_lengths, 0, new_line_lengths.size(), 1); + start_node->right = buildLineNodeTreeFromLineLengths(new_line_lengths, 0, new_line_lengths.size(), 1); } - start_node->compute_subtree_extents(); + start_node->computeSubtreeExtents(); } else if (end_node) { if (end_node->left) { delete end_node->left; @@ -75,36 +75,36 @@ void BufferOffsetIndex::Splice(unsigned start_row, unsigned deleted_lines_count, } if (!new_line_lengths.empty()) { - end_node->left = BuildLineNodeTreeFromLineLengths(new_line_lengths, 0, new_line_lengths.size(), 1); + end_node->left = buildLineNodeTreeFromLineLengths(new_line_lengths, 0, new_line_lengths.size(), 1); } - end_node->compute_subtree_extents(); + end_node->computeSubtreeExtents(); } else { if (root) { delete root; } - root = BuildLineNodeTreeFromLineLengths(new_line_lengths, 0, new_line_lengths.size(), 1); + root = buildLineNodeTreeFromLineLengths(new_line_lengths, 0, new_line_lengths.size(), 1); } if (end_node) { - end_node->compute_subtree_extents(); + end_node->computeSubtreeExtents(); } auto rng = std::uniform_int_distribution<>(1u, UINT32_MAX); if (start_node) { start_node->priority = rng(rng_engine); - BubbleNodeDown(start_node, end_node); + bubbleNodeDown(start_node, end_node); } if (end_node) { end_node->priority = rng(rng_engine); - BubbleNodeDown(end_node, nullptr); + bubbleNodeDown(end_node, nullptr); } } -unsigned BufferOffsetIndex::CharacterIndexForPosition(Point position) const { +unsigned BufferOffsetIndex::characterIndexForPosition(Point position) const { auto left_ancestor_row = 0u; auto left_ancestor_char_index = 0u; auto current_node_char_index = 0u; @@ -130,7 +130,7 @@ unsigned BufferOffsetIndex::CharacterIndexForPosition(Point position) const { } } -Point BufferOffsetIndex::PositionForCharacterIndex(unsigned char_index) const { +Point BufferOffsetIndex::positionForCharacterIndex(unsigned char_index) const { auto left_ancestor_row = 0u; auto left_ancestor_char_index = 0u; auto left_ancestor_length = 0u; @@ -160,7 +160,7 @@ Point BufferOffsetIndex::PositionForCharacterIndex(unsigned char_index) const { } } -LineNode *BufferOffsetIndex::FindAndBubbleNodeUpToRoot(unsigned row) { +LineNode *BufferOffsetIndex::findAndBubbleNodeUpToRoot(unsigned row) { auto left_ancestor_row = 0u; auto current_node = root; auto ancestors_stack = std::vector(); @@ -184,9 +184,9 @@ LineNode *BufferOffsetIndex::FindAndBubbleNodeUpToRoot(unsigned row) { ancestors_stack.pop_back(); auto root_parent = ancestors_stack.empty() ? nullptr : ancestors_stack.back(); if (root->right == current_node) { - RotateNodeLeft(current_node, root, root_parent); + rotateNodeLeft(current_node, root, root_parent); } else { // root->left == current_node - RotateNodeRight(current_node, root, root_parent); + rotateNodeRight(current_node, root, root_parent); } } @@ -196,17 +196,17 @@ LineNode *BufferOffsetIndex::FindAndBubbleNodeUpToRoot(unsigned row) { } } -void BufferOffsetIndex::BubbleNodeDown(LineNode * root, LineNode * root_parent) { +void BufferOffsetIndex::bubbleNodeDown(LineNode * root, LineNode * root_parent) { while (true) { auto left_child_priority = root->left ? root->left->priority : UINT32_MAX; auto right_child_priority = root->right ? root->right->priority : UINT32_MAX; if (left_child_priority < right_child_priority && left_child_priority < root->priority) { auto pivot = root->left; - RotateNodeRight(pivot, root, root_parent); + rotateNodeRight(pivot, root, root_parent); root_parent = pivot; } else if (right_child_priority < root->priority) { auto pivot = root->right; - RotateNodeLeft(pivot, root, root_parent); + rotateNodeLeft(pivot, root, root_parent); root_parent = pivot; } else { break; @@ -214,7 +214,7 @@ void BufferOffsetIndex::BubbleNodeDown(LineNode * root, LineNode * root_parent) } } -void BufferOffsetIndex::RotateNodeLeft(LineNode * pivot, LineNode * root, LineNode * root_parent) { +void BufferOffsetIndex::rotateNodeLeft(LineNode * pivot, LineNode * root, LineNode * root_parent) { if (root_parent) { if (root == root_parent->left) { root_parent->left = pivot; @@ -228,11 +228,11 @@ void BufferOffsetIndex::RotateNodeLeft(LineNode * pivot, LineNode * root, LineNo root->right = pivot->left; pivot->left = root; - root->compute_subtree_extents(); - pivot->compute_subtree_extents(); + root->computeSubtreeExtents(); + pivot->computeSubtreeExtents(); } -void BufferOffsetIndex::RotateNodeRight(LineNode * pivot, LineNode * root, LineNode * root_parent) { +void BufferOffsetIndex::rotateNodeRight(LineNode * pivot, LineNode * root, LineNode * root_parent) { if (root_parent) { if (root == root_parent->left) { root_parent->left = pivot; @@ -246,11 +246,11 @@ void BufferOffsetIndex::RotateNodeRight(LineNode * pivot, LineNode * root, LineN root->left = pivot->right; pivot->right = root; - root->compute_subtree_extents(); - pivot->compute_subtree_extents(); + root->computeSubtreeExtents(); + pivot->computeSubtreeExtents(); } -LineNode *BufferOffsetIndex::BuildLineNodeTreeFromLineLengths(std::vector &line_lengths, unsigned start, unsigned end, unsigned min_priority) { +LineNode *BufferOffsetIndex::buildLineNodeTreeFromLineLengths(std::vector const &line_lengths, unsigned start, unsigned end, unsigned min_priority) { if (start == end) { return nullptr; } else { @@ -258,14 +258,14 @@ LineNode *BufferOffsetIndex::BuildLineNodeTreeFromLineLengths(std::vectorcompute_subtree_extents(); + line_node->computeSubtreeExtents(); return line_node; } } diff --git a/src/buffer_offset_index.h b/src/buffer_offset_index.h index 83085d5..67c6565 100644 --- a/src/buffer_offset_index.h +++ b/src/buffer_offset_index.h @@ -11,16 +11,16 @@ class BufferOffsetIndex { public: BufferOffsetIndex(); ~BufferOffsetIndex(); - void Splice(unsigned, unsigned, std::vector&); - unsigned CharacterIndexForPosition(Point) const; - Point PositionForCharacterIndex(unsigned) const; + void splice(unsigned, unsigned, std::vector); + unsigned characterIndexForPosition(Point) const; + Point positionForCharacterIndex(unsigned) const; private: - LineNode *FindAndBubbleNodeUpToRoot(unsigned); - void BubbleNodeDown(LineNode *, LineNode *); - void RotateNodeLeft(LineNode *, LineNode *, LineNode *); - void RotateNodeRight(LineNode *, LineNode *, LineNode *); - LineNode *BuildLineNodeTreeFromLineLengths(std::vector&, unsigned, unsigned, unsigned); + LineNode *findAndBubbleNodeUpToRoot(unsigned); + void bubbleNodeDown(LineNode *, LineNode *); + void rotateNodeLeft(LineNode *, LineNode *, LineNode *); + void rotateNodeRight(LineNode *, LineNode *, LineNode *); + LineNode *buildLineNodeTreeFromLineLengths(std::vector const &, unsigned, unsigned, unsigned); LineNode *root; std::default_random_engine rng_engine; diff --git a/src/entry-browser.js b/src/entry-browser.js new file mode 100644 index 0000000..d4cb8d4 --- /dev/null +++ b/src/entry-browser.js @@ -0,0 +1,21 @@ +var setupExports = require('./entry-common'); + +var hasRan = false; + +var bind = null, lib = {}; +var asmjs = require('../build/Release/nbind.js') + +asmjs(lib, function (err, parts) { + + if (err) + return; + + hasRan = true; + bind = parts.bind; + +}); + +if (!hasRan) + throw new Error('This module hasn\'t been correctly initialized'); + +setupExports(module, bind, lib); diff --git a/src/entry-common.js b/src/entry-common.js new file mode 100644 index 0000000..fd6b337 --- /dev/null +++ b/src/entry-common.js @@ -0,0 +1,47 @@ +module.exports = function (module, bind, lib) { + + function ensurePoint(point) { + + if (Array.isArray(point)) + return new lib.Point(point[0], point[1]); + + if (point && typeof point === 'object' && !(point instanceof lib.Point)) + return new lib.Point(point.row, point.column); + + return point; + + } + + function override(target, name, fn) { + + let original = target[name]; + + Object.defineProperty(target, name, { value: function () { + + var args = [ original ]; + + for (var t = 0, T = arguments.length; t < T; ++t) + args.push(arguments[t]); + + return fn.apply(this, args); + + }, enumerable: false }); + + } + + override(lib.BufferOffsetIndex.prototype, 'characterIndexForPosition', function (original, position) { + return original.call(this, ensurePoint(position)); + }); + + bind('Point', function (row, column) { + this.row = row; this.column = column; + }); + + lib.Point.prototype.fromJS = function (output) { + output(this.row, this.column); + }; + + module.exports = lib.BufferOffsetIndex; + module.exports.Point = lib.Point; + +}; diff --git a/src/entry-node.js b/src/entry-node.js new file mode 100644 index 0000000..1ae6ed1 --- /dev/null +++ b/src/entry-node.js @@ -0,0 +1,7 @@ +var nbind = require('nbind'); +var binding = nbind.init(__dirname + '/../'); + +var bind = binding.bind, lib = binding.lib; + +var setupExports = require('./entry-common'); +setupExports(module, bind, lib); diff --git a/src/point.h b/src/point.h index 8a246a4..cc1fe5c 100644 --- a/src/point.h +++ b/src/point.h @@ -1,9 +1,40 @@ #ifndef POINT_H_ #define POINT_H_ +#include +#include "nbind/api.h" + struct Point { + unsigned row; unsigned column; + + Point(void) : row(0), column(0) { + } + + Point(unsigned initial_row, unsigned initial_column) : row(initial_row), column(initial_column) { + } + + unsigned getRow(void) const { + return row; + } + + unsigned getColumn(void) const { + return column; + } + + void setRow(unsigned new_row) { + row = new_row; + } + + void setColumn(unsigned new_column) { + column = new_column; + } + + void toJS(nbind::cbOutput output) { + output(row, column); + } + }; #endif // POINT_H_ diff --git a/test/buffer-index.test.js b/test/buffer-index.test.js index 4bd173f..ed0d6ee 100644 --- a/test/buffer-index.test.js +++ b/test/buffer-index.test.js @@ -1,40 +1,78 @@ require('segfault-handler').registerHandler() const assert = require('assert') -const BufferOffsetIndex = require('..') -const ReferenceBufferOffsetIndex = require('./reference-buffer-offset-index') -const randomSeed = require('random-seed') - -describe('BufferOffsetIndex', () => { - it('maps character indices to 2d points and viceversa', function () { - this.timeout(Infinity) - - const generateSeed = randomSeed.create() - for (var iteration = 0; iteration < 100; iteration++) { - const referenceIndex = new ReferenceBufferOffsetIndex() - const bufferIndex = new BufferOffsetIndex() - let seed = generateSeed(Number.MAX_SAFE_INTEGER) - const random = randomSeed.create(seed) - for (let i = 0; i < 50; i++) { - const startRow = random.intBetween(0, referenceIndex.lineLengths.length) - const deletedLinesCount = random.intBetween(0, referenceIndex.getLineCount() - startRow + 1) - const newLineLengths = [] - for (let j = 0; j < 10; j++) { - newLineLengths.push(random.intBetween(1, 100)) - } - - referenceIndex.splice(startRow, deletedLinesCount, newLineLengths) - bufferIndex.splice(startRow, deletedLinesCount, newLineLengths) - - for (var j = 0; j < 100; j++) { - const index = random.intBetween(0, referenceIndex.getCharactersCount()) - const row = random(10) <= 1 ? Infinity : random.intBetween(0, referenceIndex.getLineCount() + 10) - const column = random(10) <= 1 ? Infinity : random.intBetween(0, referenceIndex.getLongestColumn() + 10) - const position = {row, column} - assert.equal(bufferIndex.characterIndexForPosition(position), referenceIndex.characterIndexForPosition(position)) - assert.deepEqual(bufferIndex.positionForCharacterIndex(index), referenceIndex.positionForCharacterIndex(index)) - } - } - } - }) -}) +const ReferenceBufferOffsetIndex = require('./reference-buffer-offset-index'); + +const randomSeed = require('random-seed'); +const seed = 42; //randomSeed.create()(Number.MAX_SAFE_INTEGER); + +let passes = []; + +if (!process.env.TEST_NATIVE || process.env.TEST_NATIVE !== '0') passes.push({ + + name: 'native', + Implementation: require('../src/entry-node') + +}); + +if (!process.env.TEST_ASMJS || process.env.TEST_ASMJS !== '0') passes.push({ + + name: 'asmjs', + Implementation: require('../src/entry-browser') + +}); + +passes.forEach(({ name, Implementation }) => { + + describe('BufferOffsetIndex (' + name + ')', () => { + + it('maps character indices to 2d points and viceversa', function () { + + this.timeout(Infinity); + + for (var iteration = 0; iteration < 100; iteration++) { + + const referenceIndex = new ReferenceBufferOffsetIndex(); + const bufferIndex = new Implementation(); + + const random = randomSeed.create(seed); + + for (let i = 0; i < 50; i++) { + + const startRow = random.intBetween(0, referenceIndex.lineLengths.length); + const deletedLinesCount = random.intBetween(0, referenceIndex.getLineCount() - startRow + 1); + const newLineLengths = []; + + for (let j = 0; j < 10; j++) + newLineLengths.push(random.intBetween(1, 100)); + + referenceIndex.splice(startRow, deletedLinesCount, newLineLengths); + bufferIndex.splice(startRow, deletedLinesCount, newLineLengths); + + for (var j = 0; j < 100; j++) { + + const index = random.intBetween(0, referenceIndex.getCharactersCount()); + + const row = random(10) <= 1 ? 0xFFFFFFFF : random.intBetween(0, referenceIndex.getLineCount() + 10); + const column = random(10) <= 1 ? 0xFFFFFFFF : random.intBetween(0, referenceIndex.getLongestColumn() + 10); + + const position = { row, column }; + + const referenceCharacterIndex = referenceIndex.characterIndexForPosition(position); + assert.equal(bufferIndex.characterIndexForPosition(position), referenceCharacterIndex); + + const testedPosition = bufferIndex.positionForCharacterIndex(index); + const referencePosition = referenceIndex.positionForCharacterIndex(index); + assert.deepEqual([ testedPosition.row, testedPosition.column ], [ referencePosition.row, referencePosition.column ]); + + } + + } + + } + + }); + + }); + +});