diff --git a/fnl/conjure/school.fnl b/fnl/conjure/school.fnl index dd045ae9..8d3b5f23 100644 --- a/fnl/conjure/school.fnl +++ b/fnl/conjure/school.fnl @@ -12,11 +12,15 @@ (buffer.upsert-hidden buf-name)) (defn- append [lines] - (let [buf (upsert-buf)] - (nvim.buf_set_lines - buf - (if (buffer.empty? buf) 0 -1) - -1 false lines))) + (let [buf (upsert-buf) + current-buf-str (str.join "\n" (nvim.buf_get_lines 0 0 -1 true)) + to-insert-str (str.join "\n" lines)] + (when (not (string.find current-buf-str to-insert-str 0 true)) + (nvim.buf_set_lines + buf + (if (buffer.empty? buf) 0 -1) + -1 false lines) + true))) (defn- map-str [m] (.. (config.get-in [:mapping :prefix]) @@ -25,6 +29,11 @@ (defn- progress [n] (.. "Lesson ["n "/7] complete!")) +(defn- append-or-warn [current-progress lines] + (if (append lines) + (progress current-progress) + "You've already completed this lesson! You can (u)ndo and run it again though if you'd like.")) + (defn start [] (when (not (editor.has-filetype? :fennel)) (nvim.echo @@ -68,7 +77,8 @@ ["(school.lesson-1)"])))) (defn lesson-1 [] - (append + (append-or-warn + 1 ["" ";; Good job!" ";; You'll notice the heads up display (HUD) appeared showing the result of the evaluation." @@ -92,23 +102,23 @@ ";; Next, we have a form inside a comment. We want to evaluate that inner form, not the comment." (.. ";; Place your cursor on the inner form (the one inside the comment) and use " (map-str :eval_current_form) " to evaluate it.") "(comment" - " (school.lesson-2))"]) - (progress 1)) + " (school.lesson-2))"])) (defn lesson-2 [] - (append + (append-or-warn + 2 ["" ";; Awesome! You evaluated the inner form under your cursor." (.. ";; If we want to evaluate the outermost form under our cursor, we can use " (map-str :eval_root_form) " instead.") ";; Try that below to print some output and advance to the next lesson." ";; You can place your cursor anywhere inside the (do ...) form or it's children." "(do" - " (print \"Hello, World!\")" - " (school.lesson-3))"]) - (progress 2)) + " (print \"Hello, World!\")" + " (school.lesson-3))"])) (defn lesson-3 [] - (append + (append-or-warn + 3 ["" ";; You evaluated the outermost form! Nice!" ";; Notice that the print output was captured and displayed in the log too." @@ -116,25 +126,25 @@ (.. ";; Try pressing \"" (config.get-in [:eval :result_register]) "p to paste the contents of the register into your buffer.") (.. ";; We can also evaluate a form and replace it with the result of the evaluation with " (map-str :eval_replace_form)) (.. ";; We'll try that in the next lesson, place your cursor inside the form below and press " (map-str :eval_replace_form)) - "(school.lesson-4)"]) - (progress 3)) + "(school.lesson-4)"])) (defn lesson-4 [] - (append + (append-or-warn + 4 ["" ";; Well done! Notice how the resulting string in the log also replaced the form in the buffer!" ";; Next let's try evaluating a form at a mark." ";; Place your cursor on the next lesson form below and use mf to set the f mark at that location." (.. ";; Now move your cursor elsewhere in the buffer and use " (map-str :eval_marked_form) "f to evaluate it.") ";; If you use a capital letter like mF you can even open a different file and evaluate that marked form without changing buffers!" - "(school.lesson-5)"]) - (progress 4)) + "(school.lesson-5)"])) (def lesson-5-message "This is the contents of school.lesson-5-message!") (defn lesson-5 [] - (append + (append-or-warn + 5 ["" ";; Excellent!" ";; This is extremely useful when you want to evaluate a specific form repeatedly as you change code elsewhere in the file or project." @@ -145,14 +155,14 @@ "" (.. ";; You can evaluate visual selections with " (map-str :eval_visual)) ";; Try evaluating the form below using a visual selection." - "(school.lesson-6)"]) - (progress 5)) + "(school.lesson-6)"])) (def lesson-6-message "This is the contents of school.lesson-6-message!") (defn lesson-6 [] - (append + (append-or-warn + 6 ["" ";; Wonderful!" ";; Visual evaluation is great for specific sections of a form." @@ -161,16 +171,15 @@ "school.lesson-6-message" "" (.. ";; Use " (map-str :eval_motion) "a( to evaluate the lesson form.") - "(school.lesson-7)"]) - (progress 6)) + "(school.lesson-7)"])) (defn lesson-7 [] - (append + (append-or-warn + 7 ["" ";; Excellent job, you made it to the end!" ";; To learn more about configuring Conjure, install the plugin and check out :help conjure" ";; You can learn about specific languages with :help conjure-client- and then tab completion." ";; For example, conjure-client-fennel-aniseed or conjure-client-clojure-nrepl." "" - ";; I hope you have a wonderful time in Conjure!"]) - (progress 7)) + ";; I hope you have a wonderful time in Conjure!"])) diff --git a/lua/conjure/school.lua b/lua/conjure/school.lua index fb106e2e..c90f0ab5 100644 --- a/lua/conjure/school.lua +++ b/lua/conjure/school.lua @@ -26,13 +26,20 @@ end _2amodule_locals_2a["upsert-buf"] = upsert_buf local function append(lines) local buf = upsert_buf() - local _1_ - if buffer["empty?"](buf) then - _1_ = 0 + local current_buf_str = str.join("\n", nvim.buf_get_lines(0, 0, -1, true)) + local to_insert_str = str.join("\n", lines) + if not string.find(current_buf_str, to_insert_str, 0, true) then + local _1_ + if buffer["empty?"](buf) then + _1_ = 0 + else + _1_ = -1 + end + nvim.buf_set_lines(buf, _1_, -1, false, lines) + return true else - _1_ = -1 + return nil end - return nvim.buf_set_lines(buf, _1_, -1, false, lines) end _2amodule_locals_2a["append"] = append local function map_str(m) @@ -43,6 +50,14 @@ local function progress(n) return ("Lesson [" .. n .. "/7] complete!") end _2amodule_locals_2a["progress"] = progress +local function append_or_warn(current_progress, lines) + if append(lines) then + return progress(current_progress) + else + return "You've already completed this lesson! You can (u)ndo and run it again though if you'd like." + end +end +_2amodule_locals_2a["append-or-warn"] = append_or_warn local function start() if not editor["has-filetype?"]("fennel") then nvim.echo("Warning: No Fennel filetype found, falling back to Clojure syntax.", "Install https://github.com/Olical/aniseed for better Fennel support.") @@ -56,58 +71,51 @@ local function start() local buf = upsert_buf() nvim.ex.edit(buf_name) nvim.buf_set_lines(buf, 0, -1, false, {}) - local _4_ + local _6_ if ("" == config["get-in"]({"mapping", "prefix"})) then if a["empty?"](nvim.g.maplocalleader) then nvim.g.maplocalleader = "," nvim.ex.edit() - _4_ = {";; Your wasn't configured so I've defaulted it to comma (,) for now.", ";; See :help localleader for more information. (let maplocalleader=\",\")"} + _6_ = {";; Your wasn't configured so I've defaulted it to comma (,) for now.", ";; See :help localleader for more information. (let maplocalleader=\",\")"} else - _4_ = {(";; Your is currently mapped to \"" .. nvim.g.maplocalleader .. "\"")} + _6_ = {(";; Your is currently mapped to \"" .. nvim.g.maplocalleader .. "\"")} end else - _4_ = nil + _6_ = nil end - return append(a.concat({"(module user.conjure-school", " {require {school conjure.school}})", "", ";; Welcome to Conjure school!", ";; Grab yourself a nice beverage and let's get evaluating. I hope you enjoy!", "", ";; This language is Fennel, it's quite similar to Clojure.", ";; Conjure is written in Fennel, it's compiled to Lua and executed inside Neovim itself.", ";; This means we can work with a Lisp without installing or running anything else.", "", ";; Note: Some colorschemes will make the HUD unreadable, see here for more: https://git.io/JJ1Hl", "", ";; Let's learn how to evaluate it using Conjure's assortment of mappings.", ";; You can learn how to change these mappings with :help conjure-mappings", "", (";; Let's begin by evaluating the whole buffer using " .. map_str("eval_buf"))}, _4_, {"(school.lesson-1)"})) + return append(a.concat({"(module user.conjure-school", " {require {school conjure.school}})", "", ";; Welcome to Conjure school!", ";; Grab yourself a nice beverage and let's get evaluating. I hope you enjoy!", "", ";; This language is Fennel, it's quite similar to Clojure.", ";; Conjure is written in Fennel, it's compiled to Lua and executed inside Neovim itself.", ";; This means we can work with a Lisp without installing or running anything else.", "", ";; Note: Some colorschemes will make the HUD unreadable, see here for more: https://git.io/JJ1Hl", "", ";; Let's learn how to evaluate it using Conjure's assortment of mappings.", ";; You can learn how to change these mappings with :help conjure-mappings", "", (";; Let's begin by evaluating the whole buffer using " .. map_str("eval_buf"))}, _6_, {"(school.lesson-1)"})) end _2amodule_2a["start"] = start local function lesson_1() - append({"", ";; Good job!", ";; You'll notice the heads up display (HUD) appeared showing the result of the evaluation.", ";; All results are appended to a log buffer. If that log is not open, the HUD will appear.", ";; The HUD closes automatically when you move your cursor.", "", ";; You can open the log buffer in a few ways:", (";; * Horizontally - " .. map_str("log_split")), (";; * Vertically - " .. map_str("log_vsplit")), (";; * New tab - " .. map_str("log_tab")), "", (";; All visible log windows (including the HUD) can be closed with " .. map_str("log_close_visible")), ";; Try opening and closing the log window to get the hang of those key mappings.", ";; It's a regular window and buffer, so you can edit and close it however you want.", ";; Feel free to leave the log open in a split for the next lesson to see how it behaves.", "", ";; If you ever need to clear your log you can use the reset mappings:", (";; * Soft reset (leaves windows open) - " .. map_str("log_reset_soft")), (";; * Hard reset (closes windows, deletes the buffer) - " .. map_str("log_reset_hard")), "", ";; Next, we have a form inside a comment. We want to evaluate that inner form, not the comment.", (";; Place your cursor on the inner form (the one inside the comment) and use " .. map_str("eval_current_form") .. " to evaluate it."), "(comment", " (school.lesson-2))"}) - return progress(1) + return append_or_warn(1, {"", ";; Good job!", ";; You'll notice the heads up display (HUD) appeared showing the result of the evaluation.", ";; All results are appended to a log buffer. If that log is not open, the HUD will appear.", ";; The HUD closes automatically when you move your cursor.", "", ";; You can open the log buffer in a few ways:", (";; * Horizontally - " .. map_str("log_split")), (";; * Vertically - " .. map_str("log_vsplit")), (";; * New tab - " .. map_str("log_tab")), "", (";; All visible log windows (including the HUD) can be closed with " .. map_str("log_close_visible")), ";; Try opening and closing the log window to get the hang of those key mappings.", ";; It's a regular window and buffer, so you can edit and close it however you want.", ";; Feel free to leave the log open in a split for the next lesson to see how it behaves.", "", ";; If you ever need to clear your log you can use the reset mappings:", (";; * Soft reset (leaves windows open) - " .. map_str("log_reset_soft")), (";; * Hard reset (closes windows, deletes the buffer) - " .. map_str("log_reset_hard")), "", ";; Next, we have a form inside a comment. We want to evaluate that inner form, not the comment.", (";; Place your cursor on the inner form (the one inside the comment) and use " .. map_str("eval_current_form") .. " to evaluate it."), "(comment", " (school.lesson-2))"}) end _2amodule_2a["lesson-1"] = lesson_1 local function lesson_2() - append({"", ";; Awesome! You evaluated the inner form under your cursor.", (";; If we want to evaluate the outermost form under our cursor, we can use " .. map_str("eval_root_form") .. " instead."), ";; Try that below to print some output and advance to the next lesson.", ";; You can place your cursor anywhere inside the (do ...) form or it's children.", "(do", " (print \"Hello, World!\")", " (school.lesson-3))"}) - return progress(2) + return append_or_warn(2, {"", ";; Awesome! You evaluated the inner form under your cursor.", (";; If we want to evaluate the outermost form under our cursor, we can use " .. map_str("eval_root_form") .. " instead."), ";; Try that below to print some output and advance to the next lesson.", ";; You can place your cursor anywhere inside the (do ...) form or it's children.", "(do", " (print \"Hello, World!\")", " (school.lesson-3))"}) end _2amodule_2a["lesson-2"] = lesson_2 local function lesson_3() - append({"", ";; You evaluated the outermost form! Nice!", ";; Notice that the print output was captured and displayed in the log too.", ";; The result of every evaluation is stored in a Neovim register as well as the log.", (";; Try pressing \"" .. config["get-in"]({"eval", "result_register"}) .. "p to paste the contents of the register into your buffer."), (";; We can also evaluate a form and replace it with the result of the evaluation with " .. map_str("eval_replace_form")), (";; We'll try that in the next lesson, place your cursor inside the form below and press " .. map_str("eval_replace_form")), "(school.lesson-4)"}) - return progress(3) + return append_or_warn(3, {"", ";; You evaluated the outermost form! Nice!", ";; Notice that the print output was captured and displayed in the log too.", ";; The result of every evaluation is stored in a Neovim register as well as the log.", (";; Try pressing \"" .. config["get-in"]({"eval", "result_register"}) .. "p to paste the contents of the register into your buffer."), (";; We can also evaluate a form and replace it with the result of the evaluation with " .. map_str("eval_replace_form")), (";; We'll try that in the next lesson, place your cursor inside the form below and press " .. map_str("eval_replace_form")), "(school.lesson-4)"}) end _2amodule_2a["lesson-3"] = lesson_3 local function lesson_4() - append({"", ";; Well done! Notice how the resulting string in the log also replaced the form in the buffer!", ";; Next let's try evaluating a form at a mark.", ";; Place your cursor on the next lesson form below and use mf to set the f mark at that location.", (";; Now move your cursor elsewhere in the buffer and use " .. map_str("eval_marked_form") .. "f to evaluate it."), ";; If you use a capital letter like mF you can even open a different file and evaluate that marked form without changing buffers!", "(school.lesson-5)"}) - return progress(4) + return append_or_warn(4, {"", ";; Well done! Notice how the resulting string in the log also replaced the form in the buffer!", ";; Next let's try evaluating a form at a mark.", ";; Place your cursor on the next lesson form below and use mf to set the f mark at that location.", (";; Now move your cursor elsewhere in the buffer and use " .. map_str("eval_marked_form") .. "f to evaluate it."), ";; If you use a capital letter like mF you can even open a different file and evaluate that marked form without changing buffers!", "(school.lesson-5)"}) end _2amodule_2a["lesson-4"] = lesson_4 local lesson_5_message = "This is the contents of school.lesson-5-message!" _2amodule_2a["lesson-5-message"] = lesson_5_message local function lesson_5() - append({"", ";; Excellent!", ";; This is extremely useful when you want to evaluate a specific form repeatedly as you change code elsewhere in the file or project.", (";; Try inspecting the contents of the variable below by placing your cursor on it and pressing " .. map_str("eval_word")), "school.lesson-5-message", "", ";; You should see the contents in the HUD or log.", "", (";; You can evaluate visual selections with " .. map_str("eval_visual")), ";; Try evaluating the form below using a visual selection.", "(school.lesson-6)"}) - return progress(5) + return append_or_warn(5, {"", ";; Excellent!", ";; This is extremely useful when you want to evaluate a specific form repeatedly as you change code elsewhere in the file or project.", (";; Try inspecting the contents of the variable below by placing your cursor on it and pressing " .. map_str("eval_word")), "school.lesson-5-message", "", ";; You should see the contents in the HUD or log.", "", (";; You can evaluate visual selections with " .. map_str("eval_visual")), ";; Try evaluating the form below using a visual selection.", "(school.lesson-6)"}) end _2amodule_2a["lesson-5"] = lesson_5 local lesson_6_message = "This is the contents of school.lesson-6-message!" _2amodule_2a["lesson-6-message"] = lesson_6_message local function lesson_6() - append({"", ";; Wonderful!", ";; Visual evaluation is great for specific sections of a form.", (";; You can also evaluate a given motion with " .. map_str("eval_motion")), (";; Try " .. map_str("eval_motion") .. "iw below to evaluate the word."), "school.lesson-6-message", "", (";; Use " .. map_str("eval_motion") .. "a( to evaluate the lesson form."), "(school.lesson-7)"}) - return progress(6) + return append_or_warn(6, {"", ";; Wonderful!", ";; Visual evaluation is great for specific sections of a form.", (";; You can also evaluate a given motion with " .. map_str("eval_motion")), (";; Try " .. map_str("eval_motion") .. "iw below to evaluate the word."), "school.lesson-6-message", "", (";; Use " .. map_str("eval_motion") .. "a( to evaluate the lesson form."), "(school.lesson-7)"}) end _2amodule_2a["lesson-6"] = lesson_6 local function lesson_7() - append({"", ";; Excellent job, you made it to the end!", ";; To learn more about configuring Conjure, install the plugin and check out :help conjure", ";; You can learn about specific languages with :help conjure-client- and then tab completion.", ";; For example, conjure-client-fennel-aniseed or conjure-client-clojure-nrepl.", "", ";; I hope you have a wonderful time in Conjure!"}) - return progress(7) + return append_or_warn(7, {"", ";; Excellent job, you made it to the end!", ";; To learn more about configuring Conjure, install the plugin and check out :help conjure", ";; You can learn about specific languages with :help conjure-client- and then tab completion.", ";; For example, conjure-client-fennel-aniseed or conjure-client-clojure-nrepl.", "", ";; I hope you have a wonderful time in Conjure!"}) end _2amodule_2a["lesson-7"] = lesson_7 return _2amodule_2a \ No newline at end of file