diff --git a/src/Core/LanguageConfiguration.re b/src/Core/LanguageConfiguration.re index 9792d4ab4e..8d9d9f2a2d 100644 --- a/src/Core/LanguageConfiguration.re +++ b/src/Core/LanguageConfiguration.re @@ -92,6 +92,34 @@ module BracketPair = { let endsWithOpenPair = ({openPair, _}, str) => { StringEx.endsWith(~postfix=openPair, str); }; + + let isJustClosingPair = ({closePair, _}, str) => { + let len = String.length(str); + + let rec loop = (foundPair, idx) => + if (idx >= len) { + foundPair; + } else if (foundPair) { + false; + // We found the closing pair... but there's other stuff after + } else { + let c = str.[idx]; + + if (c == ' ' || c == '\t') { + loop(foundPair, idx + 1); + } else if (c == closePair.[0]) { + loop(true, idx + 1); + } else { + false; + }; + }; + + if (String.length(closePair) == 1) { + loop(false, 0); + } else { + false; + }; + }; }; let defaultBrackets: list(BracketPair.t) = @@ -226,9 +254,9 @@ let toVimAutoClosingPairs = (syntaxScope: SyntaxScope.t, configuration: t) => { ); }; -let toAutoIndent = +let toOpenAutoIndent = ( - {increaseIndentPattern, decreaseIndentPattern, brackets, _}, + {increaseIndentPattern, brackets, _}, ~previousLine as str, ~beforePreviousLine as _, ) => { @@ -246,16 +274,27 @@ let toAutoIndent = ) |> Option.value(~default=false); + if (increase) {Vim.AutoIndent.IncreaseIndent} else { + Vim.AutoIndent.KeepIndent + }; +}; + +let toTypeAutoIndent = ({decreaseIndentPattern, brackets, _}, str) => { let decrease = decreaseIndentPattern |> Option.map(regex => OnigRegExp.test(str, regex)) + // If no indentation pattern, fall-back to bracket pair + |> OptionEx.or_lazy(() => { + Some( + List.exists( + bracket => BracketPair.isJustClosingPair(bracket, str), + brackets, + ), + ) + }) |> Option.value(~default=false); - if (increase) { - Vim.AutoIndent.IncreaseIndent; - } else if (decrease) { - Vim.AutoIndent.DecreaseIndent; - } else { - Vim.AutoIndent.KeepIndent; + if (decrease) {Vim.AutoIndent.DecreaseIndent} else { + Vim.AutoIndent.KeepIndent }; }; diff --git a/src/Core/LanguageConfiguration.rei b/src/Core/LanguageConfiguration.rei index 213f2f8566..b0d3868e89 100644 --- a/src/Core/LanguageConfiguration.rei +++ b/src/Core/LanguageConfiguration.rei @@ -44,6 +44,8 @@ let decode: Json.decoder(t); let toVimAutoClosingPairs: (SyntaxScope.t, t) => Vim.AutoClosingPairs.t; -let toAutoIndent: +let toOpenAutoIndent: (t, ~previousLine: string, ~beforePreviousLine: option(string)) => Vim.AutoIndent.action; + +let toTypeAutoIndent: (t, string) => Vim.AutoIndent.action; diff --git a/src/Model/VimContext.re b/src/Model/VimContext.re index e5ce0eb28d..2cd260028c 100644 --- a/src/Model/VimContext.re +++ b/src/Model/VimContext.re @@ -25,7 +25,7 @@ module Internal = { |> Option.value(~default=SyntaxScope.none); }; - let autoClosingPairs = (~syntaxScope, ~maybeLanguageConfig, state: State.t) => { + let autoClosingPairs = (~syntaxScope, ~languageConfig, state: State.t) => { let acpEnabled = Configuration.getValue( c => c.editorAutoClosingBrackets, @@ -38,21 +38,15 @@ module Internal = { ); if (acpEnabled) { - maybeLanguageConfig - |> Option.map( - LanguageConfiguration.toVimAutoClosingPairs(syntaxScope), - ) - |> Option.value(~default=Vim.AutoClosingPairs.empty); + languageConfig + |> LanguageConfiguration.toVimAutoClosingPairs(syntaxScope); } else { Vim.AutoClosingPairs.empty; }; }; - let lineComment = (~maybeLanguageConfig) => { - maybeLanguageConfig - |> OptionEx.flatMap((config: LanguageConfiguration.t) => - config.lineComment - ); + let lineComment = (~languageConfig: LanguageConfiguration.t) => { + languageConfig.lineComment; }; let indentation = (~buffer) => @@ -67,12 +61,13 @@ let current = (state: State.t) => { let cursors = Editor.getVimCursors(editor); let editorBuffer = Selectors.getActiveBuffer(state); - let maybeLanguageConfig: option(LanguageConfiguration.t) = + let languageConfig: LanguageConfiguration.t = editorBuffer |> OptionEx.flatMap(Buffer.getFileType) |> OptionEx.flatMap( Ext.LanguageInfo.getLanguageConfiguration(state.languageInfo), - ); + ) + |> Option.value(~default=LanguageConfiguration.default); let maybeCursor = switch (Editor.getVimCursors(editor)) { @@ -80,17 +75,15 @@ let current = (state: State.t) => { | [] => None }; - // TODO: Hook up to Vim context - let autoIndent = - maybeLanguageConfig - |> Option.map(LanguageConfiguration.toAutoIndent) - |> Option.value(~default=(~previousLine as _, ~beforePreviousLine as _) => - Vim.AutoIndent.KeepIndent - ); + let onOpenAutoIndent = + languageConfig |> LanguageConfiguration.toOpenAutoIndent; + + let onTypeAutoIndent = + languageConfig |> LanguageConfiguration.toTypeAutoIndent; let syntaxScope = Internal.syntaxScope(~cursor=maybeCursor, state); let autoClosingPairs = - Internal.autoClosingPairs(~syntaxScope, ~maybeLanguageConfig, state); + Internal.autoClosingPairs(~syntaxScope, ~languageConfig, state); let Feature_Editor.EditorLayout.{ bufferHeightInCharacters: height, @@ -109,14 +102,15 @@ let current = (state: State.t) => { let topLine = Editor.getTopVisibleLine(editor); // Set configured line comment - let lineComment = Internal.lineComment(~maybeLanguageConfig); + let lineComment = Internal.lineComment(~languageConfig); let indentation = Internal.indentation(~buffer=editorBuffer); let insertSpaces = indentation.mode == Spaces; Vim.Context.{ - autoIndent, + onOpenAutoIndent, + onTypeAutoIndent, bufferId, leftColumn, topLine, diff --git a/src/reason-libvim/Context.re b/src/reason-libvim/Context.re index 58f1655566..f1e2d5f08a 100644 --- a/src/reason-libvim/Context.re +++ b/src/reason-libvim/Context.re @@ -1,8 +1,9 @@ type t = { autoClosingPairs: AutoClosingPairs.t, - autoIndent: + onOpenAutoIndent: (~previousLine: string, ~beforePreviousLine: option(string)) => AutoIndent.action, + onTypeAutoIndent: string => AutoIndent.action, bufferId: int, width: int, height: int, @@ -16,7 +17,8 @@ type t = { let current = () => { autoClosingPairs: AutoClosingPairs.empty, - autoIndent: (~previousLine as _, ~beforePreviousLine as _) => AutoIndent.KeepIndent, + onOpenAutoIndent: (~previousLine as _, ~beforePreviousLine as _) => AutoIndent.KeepIndent, + onTypeAutoIndent: _ => AutoIndent.KeepIndent, bufferId: Buffer.getCurrent() |> Buffer.getId, width: Window.getWidth(), height: Window.getHeight(), diff --git a/src/reason-libvim/Vim.re b/src/reason-libvim/Vim.re index 691fe62ec4..336a6cc756 100644 --- a/src/reason-libvim/Vim.re +++ b/src/reason-libvim/Vim.re @@ -28,7 +28,7 @@ module Window = Window; module Yank = Yank; module GlobalState = { - let autoIndent: + let onOpenAutoIndent: ref( option( (~previousLine: string, ~beforePreviousLine: option(string)) => @@ -175,11 +175,11 @@ let runWith = (~context: Context.t, f) => { let prevModified = Buffer.isModified(oldBuf); let prevLineEndings = Buffer.getLineEndings(oldBuf); - GlobalState.autoIndent := Some(context.autoIndent); + GlobalState.onOpenAutoIndent := Some(context.onOpenAutoIndent); let cursors = f(); - GlobalState.autoIndent := None; + GlobalState.onOpenAutoIndent := None; let newBuf = Buffer.getCurrent(); let newLocation = Cursor.getLocation(); @@ -381,7 +381,7 @@ let _onAutoIndent = (lnum: int, sourceLine: string) => { }; let indentAction = - GlobalState.autoIndent^ + GlobalState.onOpenAutoIndent^ |> Option.map(fn => fn(~previousLine=beforeLine, ~beforePreviousLine)) |> Option.value(~default=AutoIndent.KeepIndent); @@ -454,7 +454,7 @@ let init = () => { }; let input = (~context=Context.current(), v: string) => { - let {autoClosingPairs, cursors, _}: Context.t = context; + let {autoClosingPairs, cursors, onTypeAutoIndent, _}: Context.t = context; runWith( ~context, () => { @@ -466,6 +466,9 @@ let input = (~context=Context.current(), v: string) => { let location = Cursor.getLocation(); let line = Buffer.getLine(Buffer.getCurrent(), location.line); + let beforeLineNumber = location.line; + let beforeIndentAction = context.onTypeAutoIndent(line); + let isBetweenClosingPairs = () => { AutoClosingPairs.isBetweenClosingPairs( line, @@ -501,6 +504,7 @@ let input = (~context=Context.current(), v: string) => { if (String.length(precedingWhitespace) > 0) { Native.vimInput(precedingWhitespace); }; + Native.vimInput(""); } else if (AutoClosingPairs.isPassThrough( v, @@ -517,6 +521,20 @@ let input = (~context=Context.current(), v: string) => { Native.vimInput(""); } else { Native.vimInput(v); + // If we're still in insert mode, and still on the same line, check the onTypeAutoIndent for adjustments + let afterLineNumber = Cursor.getLocation().line; + let afterLine = + Buffer.getLine(Buffer.getCurrent(), afterLineNumber); + let afterIndentAction = onTypeAutoIndent(afterLine); + + if (afterLineNumber == beforeLineNumber + && afterIndentAction != beforeIndentAction) { + if (afterIndentAction == AutoIndent.IncreaseIndent) { + Native.vimInput(""); + } else if (afterIndentAction == AutoIndent.DecreaseIndent) { + Native.vimInput(""); + }; + }; }; } else { Native.vimInput(v); diff --git a/src/reason-libvim/Vim.rei b/src/reason-libvim/Vim.rei index f29255a016..0677ae510f 100644 --- a/src/reason-libvim/Vim.rei +++ b/src/reason-libvim/Vim.rei @@ -38,9 +38,13 @@ module AutoIndent: { module Context: { type t = { autoClosingPairs: AutoClosingPairs.t, - autoIndent: + // Auto-indentation engaged when a new line is entered, + // via `o`, `O`, ``, etc. + onOpenAutoIndent: (~previousLine: string, ~beforePreviousLine: option(string)) => AutoIndent.action, + // Auto-indentation engaged when the user is typing. + onTypeAutoIndent: string => AutoIndent.action, bufferId: int, width: int, height: int, diff --git a/test/Core/LanguageConfigurationTest.re b/test/Core/LanguageConfigurationTest.re index 596347e433..7b6ad739e2 100644 --- a/test/Core/LanguageConfigurationTest.re +++ b/test/Core/LanguageConfigurationTest.re @@ -87,7 +87,7 @@ describe("LanguageConfiguration", ({describe, test, _}) => { }) }); - describe("IndentationRules", ({test, _}) => { + describe("IndentationRules", ({test, describe, _}) => { test("basic parsing", ({expect, _}) => { let parsedLangConfig = json( @@ -126,62 +126,95 @@ describe("LanguageConfiguration", ({describe, test, _}) => { expect.equal(Option.is_none(langConfig.increaseIndentPattern), true); expect.equal(Option.is_some(langConfig.decreaseIndentPattern), true); }); - test("increase / decrease indent", ({expect, _}) => { - let parsedLangConfig = - json( - {| - {"indentationRules": - { - "increaseIndentPattern":"abc", - "decreaseIndentPattern":"def" - } - }|}, - ) - |> Json.Decode.decode_value(LanguageConfiguration.decode); - - expect.equal(Result.is_ok(parsedLangConfig), true); - let langConfig: LanguageConfiguration.t = - Result.get_ok(parsedLangConfig); - - expect.equal( - LanguageConfiguration.toAutoIndent( - langConfig, - ~previousLine="abc", - ~beforePreviousLine=None, - ) - == Vim.AutoIndent.IncreaseIndent, - true, - ); - expect.equal( - LanguageConfiguration.toAutoIndent( - langConfig, - ~previousLine="def", - ~beforePreviousLine=None, - ) - == Vim.AutoIndent.DecreaseIndent, - true, - ); + describe("toTypeAutoIndent", ({test, _}) => { + test("decrease indent", ({expect, _}) => { + let parsedLangConfig = + json( + {| + {"indentationRules": + { + "increaseIndentPattern":"abc", + "decreaseIndentPattern":"def" + } + }|}, + ) + |> Json.Decode.decode_value(LanguageConfiguration.decode); + + expect.equal(Result.is_ok(parsedLangConfig), true); + let langConfig: LanguageConfiguration.t = + Result.get_ok(parsedLangConfig); + + expect.equal( + LanguageConfiguration.toTypeAutoIndent(langConfig, "def"), + Vim.AutoIndent.DecreaseIndent, + ); + }); + test("falls back to brackets", ({expect, _}) => { + let parsedLangConfig = + json({| + {"brackets": [["{", "}"]]} + |}) + |> Json.Decode.decode_value(LanguageConfiguration.decode); + + expect.equal(Result.is_ok(parsedLangConfig), true); + let langConfig: LanguageConfiguration.t = + Result.get_ok(parsedLangConfig); + + expect.equal( + LanguageConfiguration.toTypeAutoIndent(langConfig, " }"), + Vim.AutoIndent.DecreaseIndent, + ); + }); }); - test("falls back to brackets", ({expect, _}) => { - let parsedLangConfig = - json({| - {"brackets": [["{", "}"]]} - |}) - |> Json.Decode.decode_value(LanguageConfiguration.decode); - - expect.equal(Result.is_ok(parsedLangConfig), true); - let langConfig: LanguageConfiguration.t = - Result.get_ok(parsedLangConfig); - - expect.equal( - LanguageConfiguration.toAutoIndent( - langConfig, - ~previousLine=" {", - ~beforePreviousLine=None, - ) - == Vim.AutoIndent.IncreaseIndent, - true, - ); + describe("toOpenAutoIndent", ({test, _}) => { + test("increase indent", ({expect, _}) => { + let parsedLangConfig = + json( + {| + {"indentationRules": + { + "increaseIndentPattern":"abc", + "decreaseIndentPattern":"def" + } + }|}, + ) + |> Json.Decode.decode_value(LanguageConfiguration.decode); + + expect.equal(Result.is_ok(parsedLangConfig), true); + let langConfig: LanguageConfiguration.t = + Result.get_ok(parsedLangConfig); + + expect.equal( + LanguageConfiguration.toOpenAutoIndent( + langConfig, + ~previousLine="abc", + ~beforePreviousLine=None, + ) + == Vim.AutoIndent.IncreaseIndent, + true, + ); + }); + test("falls back to brackets", ({expect, _}) => { + let parsedLangConfig = + json({| + {"brackets": [["{", "}"]]} + |}) + |> Json.Decode.decode_value(LanguageConfiguration.decode); + + expect.equal(Result.is_ok(parsedLangConfig), true); + let langConfig: LanguageConfiguration.t = + Result.get_ok(parsedLangConfig); + + expect.equal( + LanguageConfiguration.toOpenAutoIndent( + langConfig, + ~previousLine=" {", + ~beforePreviousLine=None, + ) + == Vim.AutoIndent.IncreaseIndent, + true, + ); + }); }); }); }); diff --git a/test/reason-libvim/AutoIndentTest.re b/test/reason-libvim/AutoIndentTest.re index f37ba37a19..b55a9e8db9 100644 --- a/test/reason-libvim/AutoIndentTest.re +++ b/test/reason-libvim/AutoIndentTest.re @@ -8,222 +8,264 @@ let resetBuffer = () => let resetBufferIndent2Spaces = () => Helpers.resetBuffer("test/reason-libvim/indent-two-spaces.txt"); -let input = (~insertSpaces=false, ~tabSize=3, ~autoIndent, s) => { - ignore( - Vim.input( - ~context={...Context.current(), insertSpaces, tabSize, autoIndent}, - s, - ): Context.t, - ); -}; - -describe("AutoIndent", ({test, _}) => { - let keepIndent = (~previousLine as _, ~beforePreviousLine: option(string)) => { - ignore(beforePreviousLine); - Vim.AutoIndent.KeepIndent; - }; - let increaseIndent = - (~previousLine as _, ~beforePreviousLine: option(string)) => { - ignore(beforePreviousLine); - Vim.AutoIndent.IncreaseIndent; - }; - let decreaseIndent = - (~previousLine as _, ~beforePreviousLine: option(string)) => { - ignore(beforePreviousLine); - Vim.AutoIndent.DecreaseIndent; - }; - - test("keep indent (open line)", ({expect, _}) => { - let buffer = resetBuffer(); - input(~autoIndent=keepIndent, "o"); - input(~autoIndent=keepIndent, "a"); - - let line = Buffer.getLine(buffer, Index.(zero + 1)); - expect.string(line).toEqual("a"); - }); - - test("increase indent (open line)", ({expect, _}) => { - let buffer = resetBuffer(); - input(~autoIndent=increaseIndent, "o"); - input(~autoIndent=increaseIndent, "a"); - - let line = Buffer.getLine(buffer, Index.(zero + 1)); - expect.string(line).toEqual("\ta"); - }); - - test("increase indent (open line, insert spaces)", ({expect, _}) => { - let buffer = resetBuffer(); - - let input = input(~insertSpaces=true, ~autoIndent=increaseIndent); - input("o"); - input("a"); - - let line = Buffer.getLine(buffer, Index.(zero + 1)); - expect.string(line).toEqual(" a"); - }); - - test("decrease indent (open line, insert spaces)", ({expect, _}) => { - let buffer = resetBuffer(); - - let input = input(~insertSpaces=true, ~autoIndent=decreaseIndent); - input("o"); - input("\t"); - input("a"); - input(""); - input("b"); - - let line = Buffer.getLine(buffer, Index.(zero + 2)); - expect.string(line).toEqual("b"); - }); - test( - "previous line is set, before previous line is None for first line", - ({expect, _}) => { - let _ = resetBuffer(); - - let prevRef = ref(""); - let beforePrevRef = ref(Some("")); - - let autoIndent = (~previousLine, ~beforePreviousLine: option(string)) => { - prevRef := previousLine; - beforePrevRef := beforePreviousLine; - AutoIndent.KeepIndent; +describe("AutoIndent", ({describe, _}) => { + describe("onTypeAutoIndent", ({test, _}) => { + let decreaseIndentWithCharacter = (c, str) => { + String.index_opt(str, c) == None + ? Vim.AutoIndent.KeepIndent : Vim.AutoIndent.DecreaseIndent; }; - - let input = input(~insertSpaces=true, ~autoIndent); - input("o"); - - expect.equal(prevRef^, "This is the first line of a test file"); - expect.equal(beforePrevRef^, None); - }); - test( - "previous line is set, before previous line is set for last line", - ({expect, _}) => { - let _ = resetBuffer(); - - let prevRef = ref(""); - let beforePrevRef = ref(Some("")); - - let autoIndent = (~previousLine, ~beforePreviousLine: option(string)) => { - prevRef := previousLine; - beforePrevRef := beforePreviousLine; - AutoIndent.KeepIndent; + let input = (~insertSpaces=true, ~tabSize=3, ~onTypeAutoIndent, s) => { + ignore( + Vim.input( + ~context={ + ...Context.current(), + insertSpaces, + tabSize, + onTypeAutoIndent, + }, + s, + ): Context.t, + ); }; - let input = input(~insertSpaces=true, ~autoIndent); - // Go to last line - input("G"); - input("o"); - - expect.equal(prevRef^, "This is the third line of a test file"); - expect.equal( - beforePrevRef^, - Some("This is the second line of a test file"), - ); - }); - - test( - "open before indented line, after empty line, keep indent", ({expect, _}) => { - let buffer = resetBufferIndent2Spaces(); - - buffer - |> Vim.Buffer.setLines( - ~lines=[| - "line 1", - "", // Add a line with spaces - " line2", - " line3", - |], - ); - - let input = input(~insertSpaces=true, ~autoIndent=keepIndent); - // Go to third line - input("gg"); - input("j"); - input("j"); - input("O"); - input("a"); - - let line = Buffer.getLine(buffer, Index.(zero + 2)); - expect.string(line).toEqual(" a"); - }); - - test("open before indented line, keep indent", ({expect, _}) => { - let buffer = resetBufferIndent2Spaces(); - - let input = input(~insertSpaces=true, ~autoIndent=keepIndent); - // Go to second line - input("j"); - input("O"); - input("a"); - - let line = Buffer.getLine(buffer, Index.(zero + 1)); - expect.string(line).toEqual("a"); - }); - - test("open before indented line, indent", ({expect, _}) => { - let buffer = resetBufferIndent2Spaces(); - - let input = input(~insertSpaces=true, ~autoIndent=increaseIndent); - // Go to second line - input("j"); - input("O"); - input("a"); - - let line = Buffer.getLine(buffer, Index.(zero + 1)); - expect.string(line).toEqual(" a"); - }); + test("decrease indent on type, conditionally", ({expect, _}) => { + let buffer = resetBuffer(); + let input = input(~onTypeAutoIndent=decreaseIndentWithCharacter('}')); - test("open before indented line, un-indent", ({expect, _}) => { - let buffer = resetBufferIndent2Spaces(); + input("O"); + input(" a"); - let input = input(~insertSpaces=true, ~autoIndent=decreaseIndent); - // Go to second line - input("j"); - input("j"); - input("O"); - input("a"); + let line = Buffer.getLine(buffer, Index.(zero)); + expect.string(line).toEqual(" a"); - let line = Buffer.getLine(buffer, Index.(zero + 2)); - expect.string(line).toEqual("a"); + input("}"); + let line = Buffer.getLine(buffer, Index.(zero)); + expect.string(line).toEqual("a}"); + }); }); - - test("open before line", ({expect, _}) => { - let _ = resetBuffer(); - - let prevRef = ref(""); - let beforePrevRef = ref(Some("")); - - let autoIndent = (~previousLine, ~beforePreviousLine: option(string)) => { - prevRef := previousLine; - beforePrevRef := beforePreviousLine; - AutoIndent.KeepIndent; + describe("onOpenAutoIndent", ({test, _}) => { + let input = (~insertSpaces=false, ~tabSize=3, ~autoIndent, s) => { + ignore( + Vim.input( + ~context={ + ...Context.current(), + insertSpaces, + tabSize, + onOpenAutoIndent: autoIndent, + }, + s, + ): Context.t, + ); }; - - let input = input(~insertSpaces=true, ~autoIndent); - // Go to last line - input("j"); - input("O"); - - expect.equal(prevRef^, "This is the first line of a test file"); - expect.equal(beforePrevRef^, None); - }); - test("auto-indent should not be called for first line", ({expect, _}) => { - let _ = resetBuffer(); - - let prevRef = ref(""); - let beforePrevRef = ref(Some("")); - let autoIndent = (~previousLine, ~beforePreviousLine) => { - prevRef := previousLine; - beforePrevRef := beforePreviousLine; - AutoIndent.KeepIndent; + let keepIndent = + (~previousLine as _, ~beforePreviousLine: option(string)) => { + ignore(beforePreviousLine); + Vim.AutoIndent.KeepIndent; + }; + let increaseIndent = + (~previousLine as _, ~beforePreviousLine: option(string)) => { + ignore(beforePreviousLine); + Vim.AutoIndent.IncreaseIndent; + }; + let decreaseIndent = + (~previousLine as _, ~beforePreviousLine: option(string)) => { + ignore(beforePreviousLine); + Vim.AutoIndent.DecreaseIndent; }; - let input = input(~insertSpaces=true, ~autoIndent); - // Open the very first line - auto-indent should not be called in this case - input("gg"); - input("O"); - - expect.equal(prevRef^, ""); - expect.equal(beforePrevRef^, None); + test("keep indent (open line)", ({expect, _}) => { + let buffer = resetBuffer(); + input(~autoIndent=keepIndent, "o"); + input(~autoIndent=keepIndent, "a"); + + let line = Buffer.getLine(buffer, Index.(zero + 1)); + expect.string(line).toEqual("a"); + }); + + test("increase indent (open line)", ({expect, _}) => { + let buffer = resetBuffer(); + input(~autoIndent=increaseIndent, "o"); + input(~autoIndent=increaseIndent, "a"); + + let line = Buffer.getLine(buffer, Index.(zero + 1)); + expect.string(line).toEqual("\ta"); + }); + + test("increase indent (open line, insert spaces)", ({expect, _}) => { + let buffer = resetBuffer(); + + let input = input(~insertSpaces=true, ~autoIndent=increaseIndent); + input("o"); + input("a"); + + let line = Buffer.getLine(buffer, Index.(zero + 1)); + expect.string(line).toEqual(" a"); + }); + + test("decrease indent (open line, insert spaces)", ({expect, _}) => { + let buffer = resetBuffer(); + + let input = input(~insertSpaces=true, ~autoIndent=decreaseIndent); + input("o"); + input("\t"); + input("a"); + input(""); + input("b"); + + let line = Buffer.getLine(buffer, Index.(zero + 2)); + expect.string(line).toEqual("b"); + }); + test( + "previous line is set, before previous line is None for first line", + ({expect, _}) => { + let _ = resetBuffer(); + + let prevRef = ref(""); + let beforePrevRef = ref(Some("")); + + let autoIndent = (~previousLine, ~beforePreviousLine: option(string)) => { + prevRef := previousLine; + beforePrevRef := beforePreviousLine; + AutoIndent.KeepIndent; + }; + + let input = input(~insertSpaces=true, ~autoIndent); + input("o"); + + expect.equal(prevRef^, "This is the first line of a test file"); + expect.equal(beforePrevRef^, None); + }); + test( + "previous line is set, before previous line is set for last line", + ({expect, _}) => { + let _ = resetBuffer(); + + let prevRef = ref(""); + let beforePrevRef = ref(Some("")); + + let autoIndent = (~previousLine, ~beforePreviousLine: option(string)) => { + prevRef := previousLine; + beforePrevRef := beforePreviousLine; + AutoIndent.KeepIndent; + }; + + let input = input(~insertSpaces=true, ~autoIndent); + // Go to last line + input("G"); + input("o"); + + expect.equal(prevRef^, "This is the third line of a test file"); + expect.equal( + beforePrevRef^, + Some("This is the second line of a test file"), + ); + }); + + test( + "open before indented line, after empty line, keep indent", + ({expect, _}) => { + let buffer = resetBufferIndent2Spaces(); + + buffer + |> Vim.Buffer.setLines( + ~lines=[| + "line 1", + "", // Add a line with spaces + " line2", + " line3", + |], + ); + + let input = input(~insertSpaces=true, ~autoIndent=keepIndent); + // Go to third line + input("gg"); + input("j"); + input("j"); + input("O"); + input("a"); + + let line = Buffer.getLine(buffer, Index.(zero + 2)); + expect.string(line).toEqual(" a"); + }); + + test("open before indented line, keep indent", ({expect, _}) => { + let buffer = resetBufferIndent2Spaces(); + + let input = input(~insertSpaces=true, ~autoIndent=keepIndent); + // Go to second line + input("j"); + input("O"); + input("a"); + + let line = Buffer.getLine(buffer, Index.(zero + 1)); + expect.string(line).toEqual("a"); + }); + + test("open before indented line, indent", ({expect, _}) => { + let buffer = resetBufferIndent2Spaces(); + + let input = input(~insertSpaces=true, ~autoIndent=increaseIndent); + // Go to second line + input("j"); + input("O"); + input("a"); + + let line = Buffer.getLine(buffer, Index.(zero + 1)); + expect.string(line).toEqual(" a"); + }); + + test("open before indented line, un-indent", ({expect, _}) => { + let buffer = resetBufferIndent2Spaces(); + + let input = input(~insertSpaces=true, ~autoIndent=decreaseIndent); + // Go to second line + input("j"); + input("j"); + input("O"); + input("a"); + + let line = Buffer.getLine(buffer, Index.(zero + 2)); + expect.string(line).toEqual("a"); + }); + + test("open before line", ({expect, _}) => { + let _ = resetBuffer(); + + let prevRef = ref(""); + let beforePrevRef = ref(Some("")); + + let autoIndent = (~previousLine, ~beforePreviousLine: option(string)) => { + prevRef := previousLine; + beforePrevRef := beforePreviousLine; + AutoIndent.KeepIndent; + }; + + let input = input(~insertSpaces=true, ~autoIndent); + // Go to last line + input("j"); + input("O"); + + expect.equal(prevRef^, "This is the first line of a test file"); + expect.equal(beforePrevRef^, None); + }); + test("auto-indent should not be called for first line", ({expect, _}) => { + let _ = resetBuffer(); + + let prevRef = ref(""); + let beforePrevRef = ref(Some("")); + let autoIndent = (~previousLine, ~beforePreviousLine) => { + prevRef := previousLine; + beforePrevRef := beforePreviousLine; + AutoIndent.KeepIndent; + }; + + let input = input(~insertSpaces=true, ~autoIndent); + // Open the very first line - auto-indent should not be called in this case + input("gg"); + input("O"); + + expect.equal(prevRef^, ""); + expect.equal(beforePrevRef^, None); + }); }); });