diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 815ae1a..d0db69a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -45,7 +45,6 @@ jobs: } mkdir -p ~/.local/share/nvim/site/pack/vendor/start git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim - git clone --depth 1 https://github.com/IlyasYOY/coredor.nvim ~/.local/share/nvim/site/pack/vendor/start/coredor.nvim git clone --depth 1 https://github.com/nvim-telescope/telescope.nvim ~/.local/share/nvim/site/pack/vendor/start/telescope.nvim git clone --depth 1 https://github.com/kyazdani42/nvim-web-devicons ~/.local/share/nvim/site/pack/vendor/start/nvim-web-devicons ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start diff --git a/README.md b/README.md index 4a48b32..bb21b2d 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ Project is currently in WIP status. This project requires: -- [IlyasYOY/coredor.nvim: Core utils for nvim](https://github.com/IlyasYOY/coredor.nvim). Utility library I use for my plugins. - [nvim-lua/plenary.nvim: plenary: full; complete; entire; absolute; unqualified. All the lua functions I don't want to write twice.](https://github.com/nvim-lua/plenary.nvim). Collection of useful utilities: testing, IO, etc. - [nvim-telescope/telescope.nvim: Find, Filter, Preview, Pick. All lua, all the time.](https://github.com/nvim-telescope/telescope.nvim). Fuzzy-searching. @@ -29,7 +28,6 @@ return { { "IlyasYOY/obs.nvim", dependencies = { - "IlyasYOY/coredor.nvim", "nvim-lua/plenary.nvim", "nvim-telescope/telescope.nvim", }, diff --git a/lua/obs/cmp-source.lua b/lua/obs/cmp-source.lua index 169243e..d9ccd66 100644 --- a/lua/obs/cmp-source.lua +++ b/lua/obs/cmp-source.lua @@ -1,4 +1,4 @@ -local core = require "coredor" +local core = require "obs.utils" local source = {} diff --git a/lua/obs/journal.lua b/lua/obs/journal.lua index 986d2ef..cc805cb 100644 --- a/lua/obs/journal.lua +++ b/lua/obs/journal.lua @@ -1,5 +1,5 @@ local Path = require "plenary.path" -local File = require "coredor.file" +local File = require "obs.utils.file" local telescope = require "obs.telescope" ---journal opts @@ -71,7 +71,7 @@ function Journal:find_journal() end -- lists journal daily entries ----@return Array +---@return Array function Journal:list_dailies() local path = self._home_path:expand() local files = File.list(path, self._date_glob .. ".md") @@ -79,7 +79,7 @@ function Journal:list_dailies() end -- lists journal weekly entries ----@return Array +---@return Array function Journal:list_weeklies() local path = self._home_path:expand() local files = File.list(path, self._week_glob .. ".md") @@ -88,7 +88,7 @@ end -- get today note file ---@param create_if_missing boolean? ----@return coredor.File +---@return obs.utils.File function Journal:today(create_if_missing) local filename = self._date_provider() ---@type Path @@ -110,7 +110,7 @@ end -- get this week note file ---@param create_if_missing boolean? ----@return coredor.File +---@return obs.utils.File function Journal:this_week(create_if_missing) local filename = self._week_provider() ---@type Path diff --git a/lua/obs/journal_spec.lua b/lua/obs/journal_spec.lua index cfe75e5..963b288 100644 --- a/lua/obs/journal_spec.lua +++ b/lua/obs/journal_spec.lua @@ -2,8 +2,8 @@ local Path = require "plenary.path" local Journal = require "obs.journal" local Templater = require "obs.templater" -local spec_utils = require "coredor.spec" -local core = require "coredor" +local spec_utils = require "obs.utils.spec" +local core = require "obs.utils" local function journal_fixture() local result = {} diff --git a/lua/obs/link_spec.lua b/lua/obs/link_spec.lua index c525877..bad6810 100644 --- a/lua/obs/link_spec.lua +++ b/lua/obs/link_spec.lua @@ -1,5 +1,5 @@ local Link = require "obs.link" -require "coredor.spec" +require "obs.utils.spec" ---@param link obs.Link ---@param header string? diff --git a/lua/obs/templater.lua b/lua/obs/templater.lua index 486eaf4..52c2c24 100644 --- a/lua/obs/templater.lua +++ b/lua/obs/templater.lua @@ -1,6 +1,6 @@ local Path = require "plenary.path" -local core = require "coredor" -local File = require "coredor.file" +local core = require "obs.utils" +local File = require "obs.utils.file" local telescope = require "obs.telescope" -- options for VarProvider @@ -97,7 +97,7 @@ function Templater:search_and_insert_template() end -- lists templates ----@return coredor.File[] +---@return obs.utils.File[] function Templater:list_templates() local home_path_string = self._home_path:expand() return File.list(home_path_string, "*.md") diff --git a/lua/obs/templater_spec.lua b/lua/obs/templater_spec.lua index 0606024..f2b00a6 100644 --- a/lua/obs/templater_spec.lua +++ b/lua/obs/templater_spec.lua @@ -1,5 +1,5 @@ local Templater = require "obs.templater" -local spec_utils = require "coredor.spec" +local spec_utils = require "obs.utils.spec" describe("proccess files", function() local templater diff --git a/lua/obs/utils.lua b/lua/obs/utils.lua deleted file mode 100644 index 0a98cfb..0000000 --- a/lua/obs/utils.lua +++ /dev/null @@ -1,78 +0,0 @@ -local File = require "coredor.file" - -local M = {} - ----lists folders under a given path ----@param path_string string? ----@return Array -function M.list_folders(path_string) - if path_string == nil then - error "path must not be nil" - end - - local file = File:new(path_string) - local plenary_file = file:as_plenary() - - if not plenary_file:exists() then - error("path '" .. path_string .. "' must exist") - end - if not plenary_file:is_dir() then - error( - "path '" .. path_string .. "' must point to directory, not a file" - ) - end - - local result = { - file:path(), - } - - -- TODO: Replace with plenary scandir. - -- somehow it didn't workout, tests were failing. - -- I guess I was misusing the API. - local files = file.list(path_string, "**/*") - - for _, found_file in ipairs(files) do - if found_file:as_plenary():is_dir() then - result[#result + 1] = found_file:path() - end - end - - return result -end - --- check it out at: https://gist.github.com/liukun/f9ce7d6d14fa45fe9b924a3eed5c3d99 --- this is a bit different version of the gist - -local function hex_to_char(x) - return string.char(tonumber(x, 16)) -end - -local function char_to_hex(c) - return string.format("%%%02X", string.byte(c)) -end - ----performs urlencoding of a given string ----@param url string? ----@return string? -function M.urlencode(url) - if url == nil then - return - end - url = url:gsub("\n", "\r\n") - url = url:gsub("([^%w])", char_to_hex) - return url -end - ----performs url decoding of a given string ----@param url string? ----@return string? -function M.urldecode(url) - if url == nil then - return - end - url = url:gsub("+", " ") - url = url:gsub("%%(%x%x)", hex_to_char) - return url -end - -return M diff --git a/lua/obs/utils/file.lua b/lua/obs/utils/file.lua new file mode 100644 index 0000000..217a34d --- /dev/null +++ b/lua/obs/utils/file.lua @@ -0,0 +1,105 @@ +local utils = require "obs.utils" +local Path = require "plenary.path" + +---Simple file wrapper +---TODO: Tests. +-- +---@class obs.utils.File +---@field private _plenary_path Path +local File = {} +File.__index = File + +---gets file name with extension +---@package +---@return string? +function File:get_name_with_extension() + local path_split = utils.string_split(self:path(), "/") + local name_with_extension = path_split[#path_split] + return name_with_extension +end + +---gets file name with out extension +---@package +---@return string? +function File:get_name_with_out_extension() + local name_with_extension = self:get_name_with_extension() + if name_with_extension == nil then + return nil + end + + local name_with_extension_split = + utils.string_split(name_with_extension, ".") + name_with_extension_split[#name_with_extension_split] = nil + local name_with_out_extension = + utils.string_merge(name_with_extension_split, ".") + + if name_with_out_extension == "" then + return name_with_extension + end + return name_with_out_extension +end + +---name of the file with out extension +---@return string? +function File:name() + return self:get_name_with_out_extension() +end + +---returns path to a file +---@return string? +function File:path() + return self._plenary_path:expand() +end + +--- lists files from path matching glob pattern +---@param path string +---@param glob string +---@return obs.utils.File[] +function File.list(path, glob) + -- NOTE: Speed this up a bit. Maybe I should use `plenary.scandir`. + local files_as_text = vim.fn.globpath(path, glob) + local files_pathes = utils.string_split(files_as_text, "\n") + local results = utils.array_map(files_pathes, function(file_path) + return File:new(file_path) + end) + return results +end + +--- creates file wrapper +---@param path string +---@return obs.utils.File +function File:new(path) + return setmetatable({ + _plenary_path = Path:new(path), + }, self) +end + +---return plenary object +---@return Path +function File:as_plenary() + return self._plenary_path +end + +---Reads file content as string +---@return string? +function File:read() + return self._plenary_path:read() +end + +---Opens file in buffer for editing +function File:edit() + vim.fn.execute("edit " .. self:path()) +end + +---renames current file. Different from mv, it does the renaming only of the file name. +--- /path/test -> to "new" -> /path/new. +--- check tests for details. +---@param new_name string name of the file +---@return boolean +function File:change_name(new_name) + local parent = self._plenary_path:parent() + local new_file_path = parent / new_name + self._plenary_path:rename { new_name = new_file_path:expand() } +end + +return File diff --git a/lua/obs/utils/file_spec.lua b/lua/obs/utils/file_spec.lua new file mode 100644 index 0000000..2c491af --- /dev/null +++ b/lua/obs/utils/file_spec.lua @@ -0,0 +1,69 @@ +---@module "luassert" +---@module "plenary.busted" + +local spec = require "obs.utils.spec" +local File = require "obs.utils.file" + +describe("change file name", function() + local dir_fixture = spec.temp_dir_fixture() + + ---resolves file in test dit with specified name + ---@param name string + ---@return obs.utils.File + local function resolve_file_with_name(name) + ---@type Path + local file_path = dir_fixture.path / name + return File:new(file_path:expand()) + end + + ---creates fime in test dir with specified name + ---@param name string + ---@return obs.utils.File + local function create_file_with_name(name) + local file = resolve_file_with_name(name) + file:as_plenary():touch() + return file + end + + it("should rename", function() + local file = create_file_with_name "test" + + local expected_name = "new name" + + file:change_name(expected_name) + + assert.file( + file, + expected_name, + resolve_file_with_name(expected_name):path() + ) + end) + + it("should rename nested directory", function() + local file = create_file_with_name "test.txt" + + local expected_name = "cool/new name" + + file:change_name(expected_name) + + assert.file( + file, + "new name", + resolve_file_with_name(expected_name):path() + ) + end) + + it("should rename with extension", function() + local file = create_file_with_name "test.txt" + + local expected_name = "new name.txt" + + file:change_name(expected_name) + + assert.file( + file, + "new name", + resolve_file_with_name(expected_name):path() + ) + end) +end) diff --git a/lua/obs/utils/init.lua b/lua/obs/utils/init.lua new file mode 100644 index 0000000..c29b9bc --- /dev/null +++ b/lua/obs/utils/init.lua @@ -0,0 +1,310 @@ +local Path = require "plenary.path" + +local M = {} + +---Merges strings to a text blob +---@param strings string[] array of strings to merge +---@param separator string? a string to be used as separator, default is '\n' +---@return string +local function string_merge(strings, separator) + if separator == nil then + separator = "\n" + end + + local result = "" + for number, line in ipairs(strings) do + result = result .. line + -- add separator in case this is not last iteration + if number ~= #strings then + result = result .. separator + end + end + + return result +end + +M.string_merge = string_merge + +---Splits string using separator. +---@param target string to split +---@param separator string +---@return string[] +function M.string_split(target, separator) + return vim.split(target, separator, { plain = true, trimempty = true }) +end + +-- Check if str starts with prefix. +---@param str string string to have prefix. +---@param prefix string? prefix itself. +---@param plain boolean? default is false +---@return boolean +local function string_has_prefix(str, prefix, plain) + if plain == nil then + plain = false + end + if prefix == nil then + return false + end + + local index = string.find(str, prefix, 1, plain) + + return index == 1 +end + +M.string_has_prefix = string_has_prefix + +-- Check if str ends with prefix. +---@param str string +---@param suffix string? +---@param plain boolean? default is false +---@return boolean +local function string_has_suffix(str, suffix, plain) + if suffix == nil then + return false + end + + return string_has_prefix(string.reverse(str), string.reverse(suffix), plain) +end + +M.string_has_suffix = string_has_suffix + +-- Strips tail if present. +-- Works on plain strings. +---@param target string +---@param tail string +---@return string +function M.string_strip_suffix(target, tail) + if not string_has_suffix(target, tail, true) then + return target + end + + return string.sub(target, 1, #target - #tail) +end + +-- Strips prefix if present. +-- Works on plain strings. +---@param target string +---@param prefix string +---@return string +function M.string_strip_prefix(target, prefix) + if not string_has_prefix(target, prefix, true) then + return target + end + + return string.sub(target, #prefix + 1, #target) +end + +-- Returns text lines selected in V mode. +---@return string[] +local function get_selected_lines() + local start_position = vim.fn.getpos "'<" + local end_position = vim.fn.getpos "'>" + local buffer_number = vim.api.nvim_get_current_buf() + + if end_position[3] == 2147483647 and start_position[3] == 1 then + return vim.api.nvim_buf_get_lines( + buffer_number, + start_position[2] - 1, + end_position[2], + false + ) + else + return vim.api.nvim_buf_get_text( + buffer_number, + start_position[2] - 1, + start_position[3] - 1, + end_position[2] - 1, + end_position[3] - 1, + {} + ) + end +end + +M.get_selected_lines = get_selected_lines + +-- Returns text selected in V mode. +---@return string +function M.get_selected_text() + local lines = get_selected_lines() + return string_merge(lines) +end + +---Returns path to current file +---@return string +function M.current_working_file() + return vim.fn.expand "%:." +end + +---Returns path to current file +---@return string +function M.current_working_file_dir() + return vim.fn.expand "%:p:h" +end + +-- Saves string to + buffer +---@param string string +function M.save_to_exchange_buffer(string) + vim.fn.setreg("+", string) +end + +---checks if file exists +---@param filename string of the file we read it +---@return boolean if the file exists +local function file_exists(filename) + ---@type Path + local path = Path:new(filename) + return path:exists() +end + +M.file_exists = file_exists + +---reades lines from file +---@param filename string of the fiel to be read +---@param processor? fun(string):string transforms strings +---@return Array transformed lines +function M.lines_from(filename, processor) + if not file_exists(filename) then + return {} + end + + local lines = {} + for line in io.lines(filename) do + if processor ~= nil then + line = processor(line) + end + lines[#lines + 1] = line + end + + return lines +end + +local uuid_template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" + +---generates uuid as string +---@return string +function M.uuid() + local uuid_string = string.gsub(uuid_template, "[xy]", function(c) + local v = (c == "x") and math.random(0, 0xf) or math.random(8, 0xb) + return string.format("%x", v) + end) + return uuid_string +end + +---maps every element of the array +---@generic T, R +---@param items T[] +---@param mapper fun(T): R +---@return R[] +function M.array_map(items, mapper) + local results = {} + for i, item in ipairs(items) do + results[i] = mapper(item) + end + return results +end + +---filters items using predicate +---@generic T +---@param items T[] +---@param predicate fun(T): boolean +function M.array_filter(items, predicate) + local results = {} + for _, item in ipairs(items) do + if predicate(item) then + results[#results + 1] = item + end + end + return results +end + +---flatmaps every element of the array +---@generic T, R +---@param items T[] +---@param mapper fun(T): R[] +---@return R[] +function M.array_flat_map(items, mapper) + local results = {} + for _, item in ipairs(items) do + local mapped_items = mapper(item) + for _, mapped_item in ipairs(mapped_items) do + results[#results + 1] = mapped_item + end + end + return results +end + +---lists folders under a given path +---@param path_string string? +---@return Array +function M.list_folders(path_string) + if path_string == nil then + error "path must not be nil" + end + + local File = require "obs.utils.file" + local file = File:new(path_string) + local plenary_file = file:as_plenary() + + if not plenary_file:exists() then + error("path '" .. path_string .. "' must exist") + end + if not plenary_file:is_dir() then + error( + "path '" .. path_string .. "' must point to directory, not a file" + ) + end + + local result = { + file:path(), + } + + -- TODO: Replace with plenary scandir. + -- somehow it didn't workout, tests were failing. + -- I guess I was misusing the API. + local files = file.list(path_string, "**/*") + + for _, found_file in ipairs(files) do + if found_file:as_plenary():is_dir() then + result[#result + 1] = found_file:path() + end + end + + return result +end + +-- check it out at: https://gist.github.com/liukun/f9ce7d6d14fa45fe9b924a3eed5c3d99 +-- this is a bit different version of the gist + +local function hex_to_char(x) + return string.char(tonumber(x, 16)) +end + +local function char_to_hex(c) + return string.format("%%%02X", string.byte(c)) +end + +---performs urlencoding of a given string +---@param url string? +---@return string? +function M.urlencode(url) + if url == nil then + return + end + url = url:gsub("\n", "\r\n") + url = url:gsub("([^%w])", char_to_hex) + return url +end + +---performs url decoding of a given string +---@param url string? +---@return string? +function M.urldecode(url) + if url == nil then + return + end + url = url:gsub("+", " ") + url = url:gsub("%%(%x%x)", hex_to_char) + return url +end + +return M diff --git a/lua/obs/utils/init_spec.lua b/lua/obs/utils/init_spec.lua new file mode 100644 index 0000000..1770893 --- /dev/null +++ b/lua/obs/utils/init_spec.lua @@ -0,0 +1,343 @@ +local utils = require "obs.utils" +local spec = require "obs.utils.spec" + +describe("list directories", function() + local list_directories = utils.list_folders + local temp_file_fixture = spec.temp_file_fixture() + local temp_dir_fixture = spec.temp_dir_fixture() + + it("nil parameter causes error", function() + assert.has_error(function() + list_directories() + end, "path must not be nil") + end) + + it("not existing directory causes error", function() + local invalid_directory_path = "invalid directory " .. utils.uuid() + + assert.has_error(function() + list_directories(invalid_directory_path) + end, "path '" .. invalid_directory_path .. "' must exist") + end) + + it("not directory causes error", function() + local file_path = temp_file_fixture.path:expand() + + assert.has_error(function() + list_directories(file_path) + end, "path '" .. file_path .. "' must point to directory, not a file") + end) + + it("get current directory", function() + local temp_dir_path = temp_dir_fixture.path:expand() + + local result = list_directories(temp_dir_path) + + assert.is_not_nil(result) + assert.list_size(result, 1) + assert.are.equal(temp_dir_path, result[1]) + end) + + it("get current directory with nested directory", function() + local temp_dir_path = temp_dir_fixture.path + local nested_dir = temp_dir_fixture.path / utils.uuid() + nested_dir:mkdir() + + local result = list_directories(temp_dir_path:expand()) + + assert.is_not_nil(result) + assert.list_size(result, 2) + assert.are.equal(temp_dir_path:expand(), result[1]) + assert.are.equal(nested_dir:expand(), result[2]) + end) + + it("get current directory with 2 nested directories", function() + local temp_dir_path = temp_dir_fixture.path + local nested_dir1 = temp_dir_fixture.path / utils.uuid() + nested_dir1:mkdir() + local nested_dir2 = temp_dir_fixture.path / utils.uuid() + nested_dir2:mkdir() + + local result = list_directories(temp_dir_path:expand()) + + assert.is_not_nil(result) + assert.list_size(result, 3) + assert.are.equal(temp_dir_path:expand(), result[1]) + assert( + nested_dir1:expand() == result[2] + and nested_dir2:expand() == result[3] + or nested_dir1:expand() == result[3] + and nested_dir2:expand() == result[2] + ) + end) + + it("get current directory with 2 inner nested directories", function() + local temp_dir_path = temp_dir_fixture.path + local nested_dir1 = temp_dir_fixture.path / utils.uuid() + nested_dir1:mkdir() + local nested_dir2 = nested_dir1 / utils.uuid() + nested_dir2:mkdir() + + local result = list_directories(temp_dir_path:expand()) + + assert.is_not_nil(result) + assert.list_size(result, 3) + assert.are.equal(temp_dir_path:expand(), result[1]) + assert( + nested_dir1:expand() == result[2] + and nested_dir2:expand() == result[3] + or nested_dir1:expand() == result[3] + and nested_dir2:expand() == result[2] + ) + end) + + it("get current directory with nested directory and file", function() + local temp_dir_path = temp_dir_fixture.path + local nested_dir1 = temp_dir_fixture.path / utils.uuid() + nested_dir1:mkdir() + local nested_file = temp_dir_fixture.path / utils.uuid() + nested_file:touch() + + local result = list_directories(temp_dir_path:expand()) + + assert.is_not_nil(result) + assert.list_size(result, 2) + assert.are.equal(temp_dir_path:expand(), result[1]) + assert.are.equal(nested_dir1:expand(), result[2]) + end) + + local urlencode = utils.urlencode + local urldecode = utils.urldecode + it("url encode", function() + local result = urlencode "привет мир" + + assert.are.equal( + "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80", + result + ) + end) + + it("url decode", function() + local result = + urldecode "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80" + + assert.are.equal("привет мир", result) + end) + + it("url decode/encode", function() + local str = "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80" + local decoded = urldecode(str) + local encoded = urlencode(decoded) + assert.are.equal(str, encoded) + end) +end) +describe("string", function() + describe("has suffix", function() + it("nil parameter", function() + assert.is_false( + utils.string_has_suffix("test", nil), + "must returns nil when parameter is nil" + ) + end) + + for _, str in ipairs { + "test.txt", + "test.md.txt", + "testmd", + } do + it("'" .. str .. "' is false for '.md' suffix", function() + assert.is_false( + utils.string_has_suffix(str, ".md", true), + "prefix was found even though it was absent" + ) + end) + end + + it("is true", function() + assert.is_true( + utils.string_has_suffix("test.txt", ".txt", true), + "prefix was not found" + ) + end) + end) + + describe("split", function() + local string_split = utils.string_split + + local function check_hello_world_split(split) + assert.are.equal(2, #split, "string should be spit in two words") + assert.are.equal("hello", split[1], "first word is wrong") + assert.are.equal("world", split[2], "second word is wrong") + end + + it("no splits found", function() + local result = string_split("hello world", "x") + assert.are.equal( + 1, + #result, + "string should not be split, but it was" + ) + assert.are.equal( + "hello world", + result[#result], + "string itself is the only element of the array" + ) + end) + + it("works with space", function() + local result = string_split("hello world", " ") + check_hello_world_split(result) + end) + + it("works with .", function() + local result = string_split("hello.world", ".") + check_hello_world_split(result) + end) + + it("work with new line separator", function() + local result = string_split("hello\nworld", "\n") + check_hello_world_split(result) + end) + end) + + describe("starts with", function() + local starts_with = utils.string_has_prefix + + it("prefix param is nil", function() + local result = starts_with("abc test", nil) + assert.is_false(result, "false must be returned on nil input") + end) + + it("found prefix", function() + local result = starts_with("abc test", "abc") + assert.is_true(result, "prefix must be found") + end) + + for _, str in ipairs { + "test abc ", + "test abcd", + } do + it("'abc' not prefix of '" .. str .. "'", function() + local result = starts_with(str, "abc") + assert.is_false( + result, + "prefix should not be found, string not in the end" + ) + end) + end + end) + + describe("strip prefix", function() + local strip_prefix = utils.string_strip_prefix + + it("not in prefix", function() + local result = strip_prefix("aaa abc", "abc") + assert.are.equal( + "aaa abc", + result, + "string is no in prefix, it's suffix" + ) + end) + + it("not in string", function() + local result = strip_prefix("aaa bbb", "abc") + assert.are.equal("aaa bbb", result, "prefix should not be found in") + end) + + it("remove prefix ", function() + local result = strip_prefix("abc aaa", "abc") + assert.are.equal(" aaa", result, "prefix was removed incorrectly") + end) + end) +end) + +describe("array operation", function() + local map = utils.array_map + + describe("map", function() + for name, case in pairs { + empty = { + input = {}, + expected = {}, + }, + singleton = { + input = { 1 }, + expected = { 2 }, + }, + big = { + input = { 1, 7, 16 }, + expected = { 2, 14, 32 }, + }, + } do + it(name .. " list", function() + local result = map(case.input, function(x) + return x * 2 + end) + assert.is_true(vim.deep_equal(result, case.expected)) + end) + end + end) + + describe("filter", function() + local filter = utils.array_filter + + for name, case in pairs { + empty = { + input = {}, + expected = {}, + }, + ["singleton not passes"] = { + input = { 1 }, + expected = {}, + }, + ["singleton passes"] = { + input = { 2 }, + expected = { 2 }, + }, + ["some pass some not"] = { + input = { 1, 7, 8, 4, 9, 10 }, + expected = { 8, 4, 10 }, + }, + } do + it(name .. " list", function() + local result = filter(case.input, function(x) + return x % 2 == 0 + end) + assert.is_true(vim.deep_equal(result, case.expected)) + end) + end + end) + + describe("flat_map", function() + local flat_map = utils.array_flat_map + + for name, case in pairs { + empty = { + input = {}, + expected = {}, + }, + ["single item"] = { + input = { 2 }, + expected = { 2, 4 }, + }, + ["multiple items"] = { + input = { 2, 7, 3 }, + expected = { 2, 4, 7, 14, 3, 6 }, + }, + } do + it(name .. " list", function() + local result = flat_map(case.input, function(x) + return { x, x * 2 } + end) + assert.is_true( + vim.deep_equal(result, case.expected), + string.format( + "Expected %s, but was %s", + vim.inspect(case.expected), + vim.inspect(result) + ) + ) + end) + end + end) +end) diff --git a/lua/obs/utils/spec.lua b/lua/obs/utils/spec.lua new file mode 100644 index 0000000..3e987b0 --- /dev/null +++ b/lua/obs/utils/spec.lua @@ -0,0 +1,67 @@ +local Path = require "plenary.path" +local utils = require "obs.utils" + +local M = {} + +---@class obs.utils.spec.Path +---@field public path Path path to the directory + +---Creates empty file per test +---@return obs.utils.spec.Path +function M.temp_file_fixture() + local result = {} + + before_each(function() + local tmp_file_name = "/tmp/lua-" .. utils.uuid() + result.path = Path:new(tmp_file_name) + if not result.path:touch() then + error "cannot create temp file" + end + end) + + after_each(function() + os.execute("rm -f " .. result.path:expand()) + end) + + return result +end + +--- Creates empty directory per test +---@return obs.utils.spec.Path +function M.temp_dir_fixture() + local result = {} + + before_each(function() + local tmp_file_name = "/tmp/lua-" .. utils.uuid() + result.path = Path:new(tmp_file_name) + if not result.path:mkdir() then + error "cannot create temp file" + end + end) + + after_each(function() + os.execute("rm -rf " .. result.path:expand()) + end) + + return result +end + +---@generic T +---@param list T[] +---@param size number +function M.assert_list_size(list, size) + assert.are.equal(size, #list, "wrong number of etries") +end + +---@param file obs.utils.File +---@param name string? +---@param path string? +function M.assert_file(file, name, path) + assert.are.equal(name, file:name(), "wrong file name") + assert.are.equal(path, file:path(), "wrong file path") +end + +assert.list_size = M.assert_list_size +assert.file = M.assert_file + +return M diff --git a/lua/obs/utils_spec.lua b/lua/obs/utils_spec.lua deleted file mode 100644 index e77b31a..0000000 --- a/lua/obs/utils_spec.lua +++ /dev/null @@ -1,134 +0,0 @@ -local spec = require "coredor.spec" -local coredor = require "coredor" - -describe("list directories", function() - local utils = require "obs.utils" - local list_directories = utils.list_folders - local temp_file_fixture = spec.temp_file_fixture() - local temp_dir_fixture = spec.temp_dir_fixture() - - it("nil parameter causes error", function() - assert.has_error(function() - list_directories() - end, "path must not be nil") - end) - - it("not existing directory causes error", function() - local invalid_directory_path = "invalid directory " .. coredor.uuid() - - assert.has_error(function() - list_directories(invalid_directory_path) - end, "path '" .. invalid_directory_path .. "' must exist") - end) - - it("not directory causes error", function() - local file_path = temp_file_fixture.path:expand() - - assert.has_error(function() - list_directories(file_path) - end, "path '" .. file_path .. "' must point to directory, not a file") - end) - - it("get current directory", function() - local temp_dir_path = temp_dir_fixture.path:expand() - - local result = list_directories(temp_dir_path) - - assert.is_not_nil(result) - assert.list_size(result, 1) - assert.are.equal(temp_dir_path, result[1]) - end) - - it("get current directory with nested directory", function() - local temp_dir_path = temp_dir_fixture.path - local nested_dir = temp_dir_fixture.path / coredor.uuid() - nested_dir:mkdir() - - local result = list_directories(temp_dir_path:expand()) - - assert.is_not_nil(result) - assert.list_size(result, 2) - assert.are.equal(temp_dir_path:expand(), result[1]) - assert.are.equal(nested_dir:expand(), result[2]) - end) - - it("get current directory with 2 nested directories", function() - local temp_dir_path = temp_dir_fixture.path - local nested_dir1 = temp_dir_fixture.path / coredor.uuid() - nested_dir1:mkdir() - local nested_dir2 = temp_dir_fixture.path / coredor.uuid() - nested_dir2:mkdir() - - local result = list_directories(temp_dir_path:expand()) - - assert.is_not_nil(result) - assert.list_size(result, 3) - assert.are.equal(temp_dir_path:expand(), result[1]) - assert( - nested_dir1:expand() == result[2] - and nested_dir2:expand() == result[3] - or nested_dir1:expand() == result[3] - and nested_dir2:expand() == result[2] - ) - end) - - it("get current directory with 2 inner nested directories", function() - local temp_dir_path = temp_dir_fixture.path - local nested_dir1 = temp_dir_fixture.path / coredor.uuid() - nested_dir1:mkdir() - local nested_dir2 = nested_dir1 / coredor.uuid() - nested_dir2:mkdir() - - local result = list_directories(temp_dir_path:expand()) - - assert.is_not_nil(result) - assert.list_size(result, 3) - assert.are.equal(temp_dir_path:expand(), result[1]) - assert( - nested_dir1:expand() == result[2] - and nested_dir2:expand() == result[3] - or nested_dir1:expand() == result[3] - and nested_dir2:expand() == result[2] - ) - end) - - it("get current directory with nested directory and file", function() - local temp_dir_path = temp_dir_fixture.path - local nested_dir1 = temp_dir_fixture.path / coredor.uuid() - nested_dir1:mkdir() - local nested_file = temp_dir_fixture.path / coredor.uuid() - nested_file:touch() - - local result = list_directories(temp_dir_path:expand()) - - assert.is_not_nil(result) - assert.list_size(result, 2) - assert.are.equal(temp_dir_path:expand(), result[1]) - assert.are.equal(nested_dir1:expand(), result[2]) - end) - - local urlencode = utils.urlencode - local urldecode = utils.urldecode - it("url encode", function() - local result = urlencode "привет мир" - - assert.are.equal( - "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80", - result - ) - end) - - it("url decode", function() - local result = - urldecode "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80" - - assert.are.equal("привет мир", result) - end) - - it("url decode/encode", function() - local str = "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80" - local decoded = urldecode(str) - local encoded = urlencode(decoded) - assert.are.equal(str, encoded) - end) -end) diff --git a/lua/obs/vault.lua b/lua/obs/vault.lua index b06b06d..033ff8a 100644 --- a/lua/obs/vault.lua +++ b/lua/obs/vault.lua @@ -1,11 +1,11 @@ local Link = require "obs.link" local Templater = require "obs.templater" -local File = require "coredor.file" +local File = require "obs.utils.file" local Journal = require "obs.journal" local Path = require "plenary.path" local obs_telescope = require "obs.telescope" -local core = require "coredor" +local core = require "obs.utils" local utils = require "obs.utils" -- table with vault options @@ -102,7 +102,7 @@ end ---create notes with defaults to name structure applied ---@param name string? ----@return coredor.File? +---@return obs.utils.File? function Vault:create_note(name) local time = self._time_provider() if not name or name == "" then @@ -174,7 +174,7 @@ end ---renames the note ---@param name string ---@param new_name string ----@return coredor.File? +---@return obs.utils.File? function Vault:rename(name, new_name) local note = self:get_note(name) if note == nil then @@ -370,7 +370,7 @@ end ---get note from vault using name of the file ---@param name string ----@return coredor.File? +---@return obs.utils.File? function Vault:get_note(name) local notes = self:list_notes() @@ -392,21 +392,21 @@ function Vault:open_weekly() end ---lists notes from vault ----@return coredor.File[] +---@return obs.utils.File[] function Vault:list_notes() return File.list(self._home_path:expand(), "**/*.md") end ---lists backlinks to a note using name ---@param name string ----@return coredor.File[] +---@return obs.utils.File[] function Vault:list_backlinks(name) local note_for_name = self:get_note(name) if note_for_name == nil then return {} end - ---@type coredor.File[] + ---@type obs.utils.File[] local notes_with_backlinks = {} local notes = self:list_notes() for _, note in ipairs(notes) do diff --git a/lua/obs/vault_spec.lua b/lua/obs/vault_spec.lua index d4721a3..4d961a3 100644 --- a/lua/obs/vault_spec.lua +++ b/lua/obs/vault_spec.lua @@ -1,6 +1,6 @@ local Vault = require "obs.vault" -local spec = require "coredor.spec" -local File = require "coredor.file" +local spec = require "obs.utils.spec" +local File = require "obs.utils.file" local function vault_fixture() local result = {} @@ -23,7 +23,7 @@ local function vault_fixture() ---creates file in vault ---@param name string file name - ---@return coredor.File + ---@return obs.utils.File function result.create_file(name) ---@type Path local file_path = (vault_home.path / name) diff --git a/scripts/minimal_init.lua b/scripts/minimal_init.lua index baa4661..23434f4 100644 --- a/scripts/minimal_init.lua +++ b/scripts/minimal_init.lua @@ -30,7 +30,6 @@ function M.setup() vim.opt.packpath = { M.root(".tests/site") } M.load("nvim-lua/plenary.nvim") - M.load("IlyasYOY/coredor.nvim") M.load("nvim-telescope/telescope.nvim") end