diff --git a/src/editor/Core/Actions.re b/src/editor/Core/Actions.re index 345e628c21..2c3f65802b 100644 --- a/src/editor/Core/Actions.re +++ b/src/editor/Core/Actions.re @@ -4,10 +4,10 @@ * Encapsulates actions that can impact the editor state */ -open State; open Types; type t = + | BufferUpdate(BufferUpdate.t) | ChangeMode(Mode.t) | SetEditorFont(EditorFont.t) | Noop; diff --git a/src/editor/Core/Buffer.re b/src/editor/Core/Buffer.re index cf868263d6..73d1d08297 100644 --- a/src/editor/Core/Buffer.re +++ b/src/editor/Core/Buffer.re @@ -4,6 +4,8 @@ * In-memory text buffer representation */ +open Types; + type t = { file: option(string), lines: array(string), @@ -13,3 +15,43 @@ let ofLines = (lines: array(string)) => { let ret: t = {file: None, lines}; ret; }; + +let slice = (~lines: array(string), ~start, ~length, ()) => { + let len = Array.length(lines); + if (start >= len) { + [||]; + } else { + let len = min(start + length, len) - start; + if (len <= 0) { + [||]; + } else { + Array.sub(lines, start, len); + }; + }; +}; + +let applyUpdate = (lines: array(string), update: BufferUpdate.t) => { + + if (update.endLine <= update.startLine) { + Array.of_list(update.lines) + } else { + + let prev = slice(~lines, ~start=0, ~length=update.startLine, ()); + let post = + slice( + ~lines, + ~start=update.endLine, + ~length=Array.length(lines) - update.endLine, + (), + ); + + let lines = Array.of_list(update.lines); + + Array.concat([prev, lines, post]); + } +}; + +let update = (buf: t, update: BufferUpdate.t) => { + let ret: t = {...buf, lines: applyUpdate(buf.lines, update)}; + ret; +}; diff --git a/src/editor/Core/Reducer.re b/src/editor/Core/Reducer.re index e6fa492c26..34fd6967ea 100644 --- a/src/editor/Core/Reducer.re +++ b/src/editor/Core/Reducer.re @@ -12,6 +12,7 @@ let reduce: (State.t, Actions.t) => State.t = | ChangeMode(m) => let ret: State.t = {...s, mode: m}; ret; + | BufferUpdate(bu) => {...s, buffer: Buffer.update(s.buffer, bu)} | SetEditorFont(font) => {...s, editorFont: font} | Noop => s }; diff --git a/src/editor/Core/State.re b/src/editor/Core/State.re index 023801435b..7534a12606 100644 --- a/src/editor/Core/State.re +++ b/src/editor/Core/State.re @@ -5,21 +5,6 @@ */ open Types; - -module Mode = { - type t = - | Insert - | Normal - | Other; - - let show = v => - switch (v) { - | Insert => "insert" - | Normal => "normal" - | Other => "unknown" - }; -}; - module Tab = { type t = { id: int, @@ -33,12 +18,14 @@ module Tab = { type t = { mode: Mode.t, tabs: list(Tab.t), + buffer: Buffer.t, editorFont: EditorFont.t, }; let create: unit => t = () => { mode: Insert, + buffer: Buffer.ofLines([|"testing"|]), editorFont: EditorFont.create( ~fontFile="FiraCode-Regular.ttf", diff --git a/src/editor/Core/Types.re b/src/editor/Core/Types.re index c121b42d11..2b275507b2 100644 --- a/src/editor/Core/Types.re +++ b/src/editor/Core/Types.re @@ -10,6 +10,34 @@ module Position = { }; }; +module Mode = { + type t = + | Insert + | Normal + | Other; + + let show = v => + switch (v) { + | Insert => "insert" + | Normal => "normal" + | Other => "unknown" + }; +}; + +module BufferUpdate = { + type t = { + startLine: int, + endLine: int, + lines: list(string), + }; + + let create = (~startLine, ~endLine, ~lines, ()) => { + startLine, + endLine, + lines, + }; +}; + module EditorFont = { type t = { fontFile: string, diff --git a/src/editor/UI/EditorSurface.re b/src/editor/UI/EditorSurface.re index 4b5df2e0c6..401bdb9ee8 100644 --- a/src/editor/UI/EditorSurface.re +++ b/src/editor/UI/EditorSurface.re @@ -69,12 +69,7 @@ let make = (state: State.t) => component((_slots: React.Hooks.empty) => { let theme = Theme.get(); - let bufferView = - Buffer.ofLines([| - "- Hello from line 1", - "- Hello from line 2", - "--- Hello from line 3", - |]) + let bufferView = state.buffer |> TokenizedBuffer.ofBuffer |> TokenizedBufferView.ofTokenizedBuffer; @@ -91,7 +86,6 @@ let make = (state: State.t) => color(theme.foreground), flexGrow(1), ]; - /* flexShrink(1), */ ...textElements ; }); diff --git a/src/editor/UI/StatusBar.re b/src/editor/UI/StatusBar.re index 6229df7129..d367a1cdaa 100644 --- a/src/editor/UI/StatusBar.re +++ b/src/editor/UI/StatusBar.re @@ -25,10 +25,10 @@ let viewStyle = alignItems(`Center), ]; -let make = (~mode: State.Mode.t, ()) => +let make = (~mode: Types.Mode.t, ()) => component((_slots: React.Hooks.empty) => - + ); diff --git a/src/editor/bin/Oni2.re b/src/editor/bin/Oni2.re index c808ed3b0f..31dd2e9c6c 100644 --- a/src/editor/bin/Oni2.re +++ b/src/editor/bin/Oni2.re @@ -40,7 +40,7 @@ let init = app => { let state: Core.State.t = App.getState(app); prerr_endline( "[DEBUG - STATE] Mode: " - ++ Core.State.Mode.show(state.mode) + ++ Core.Types.Mode.show(state.mode) ++ " editor font measured width: " ++ string_of_int(state.editorFont.measuredWidth) ++ " editor font measured height: " @@ -154,6 +154,7 @@ let init = app => { | ModeChanged("normal") => Core.Actions.ChangeMode(Normal) | ModeChanged("insert") => Core.Actions.ChangeMode(Insert) | ModeChanged(_) => Core.Actions.ChangeMode(Other) + | BufferLines(bc) => Core.Actions.BufferUpdate(Core.Types.BufferUpdate.create(~startLine=bc.firstLine, ~endLine=bc.lastLine, ~lines=bc.lines, ())) | _ => Noop }; diff --git a/test/editor/Core/BufferTests.re b/test/editor/Core/BufferTests.re new file mode 100644 index 0000000000..14507fc345 --- /dev/null +++ b/test/editor/Core/BufferTests.re @@ -0,0 +1,37 @@ +/* open Oni_Core; */ +open TestFramework; + +open Helpers; + +open Oni_Core.Types; +module Buffer = Oni_Core.Buffer; + +describe("update", ({test, _}) => { + test("empty buffer w/ update", ({expect}) => { + let buffer = Buffer.ofLines([||]); + let update = BufferUpdate.create(~startLine=0, ~endLine=1, ~lines=["a"], ()); + let updatedBuffer = Buffer.update(buffer, update); + validateBuffer(expect, updatedBuffer, [|"a"|]); + }); + + test("update single line", ({expect}) => { + let buffer = Buffer.ofLines([|"a"|]); + let update = BufferUpdate.create(~startLine=0, ~endLine=1, ~lines=["abc"], ()); + let updatedBuffer = Buffer.update(buffer, update); + validateBuffer(expect, updatedBuffer, [|"abc"|]); + }); + + test("delete line", ({expect}) => { + let buffer = Buffer.ofLines([|"a"|]); + let update = BufferUpdate.create(~startLine=0, ~endLine=1, ~lines=[], ()); + let updatedBuffer = Buffer.update(buffer, update); + validateBuffer(expect, updatedBuffer, [||]); + }); + + test("update single line", ({expect}) => { + let buffer = Buffer.ofLines([|"a", "b", "c"|]); + let update = BufferUpdate.create(~startLine=1, ~endLine=2, ~lines=["d", "e", "f"], ()); + let updatedBuffer = Buffer.update(buffer, update); + validateBuffer(expect, updatedBuffer, [|"a", "d", "e", "f", "c"|]); + }); +}); diff --git a/test/editor/Core/Helpers.re b/test/editor/Core/Helpers.re index 1534401b14..eb4c597008 100644 --- a/test/editor/Core/Helpers.re +++ b/test/editor/Core/Helpers.re @@ -1,6 +1,8 @@ open Oni_Core; open Oni_Core.Types; +module Buffer = Oni_Core.Buffer; + let validateToken = ( expect: Rely__DefaultMatchers.matchers(unit), @@ -30,3 +32,22 @@ let validateTokens = List.iter2(f, actualTokens, expectedTokens); }; + +let validateBuffer = + ( + expect: Rely__DefaultMatchers.matchers(unit), + actualBuffer: Buffer.t, + expectedLines: array(string), + ) => { + expect.int(Array.length(actualBuffer.lines)).toBe(Array.length(expectedLines)); + + let validateLine = (actualLine, expectedLine) => { + expect.string(actualLine).toEqual(expectedLine); + } + + let f = (actual, expected) => { + validateLine(actual, expected); + }; + + Array.iter2(f, actualBuffer.lines, expectedLines); +};