diff --git a/lua/java-core/adapters/init.lua b/lua/java-core/adapters/init.lua index 6c93a37..f14161b 100644 --- a/lua/java-core/adapters/init.lua +++ b/lua/java-core/adapters/init.lua @@ -1,5 +1,3 @@ -local jdtls_types = require('java-core.ls.servers.jdtls.jdtls-types') - local List = require('java-core.utils.list') local Set = require('java-core.utils.set') local JavaTestClient = require('java-core.ls.clients.java-test-client') @@ -10,7 +8,7 @@ local M = {} ---@param launch_args JavaCoreTestJunitLaunchArguments ---@param java_exec string ---@param config { debug: boolean, label: string } ----@return JavaCoreDapLauncherConfig +---@return java_core.DapLauncherConfig function M.get_dap_launcher_config(launch_args, java_exec, config) return { name = config.label, diff --git a/lua/java-core/api/test.lua b/lua/java-core/api/test.lua index b3f67e4..c95ba0a 100644 --- a/lua/java-core/api/test.lua +++ b/lua/java-core/api/test.lua @@ -1,21 +1,19 @@ local log = require('java-core.utils.log') local data_adapters = require('java-core.adapters') -local TestReport = require('java-core.dap.test-report') -local JavaDapRunner = require('java-core.dap.runner') local JavaDebug = require('java-core.ls.clients.java-debug-client') local JavaTest = require('java-core.ls.clients.java-test-client') ----@class JavaCoreTestApi +---@class java_core.TestApi ---@field private client java_core.JdtlsClient ---@field private debug_client JavaCoreDebugClient ---@field private test_client java_core.TestClient ----@field private runner JavaCoreDapRunner +---@field private runner java_core.DapRunner local M = {} ---Returns a new test helper client ----@param args { client: LspClient } ----@return JavaCoreTestApi +---@param args { client: LspClient, runner: java_core.DapRunner } +---@return java_core.TestApi function M:new(args) local o = { client = args.client, @@ -29,9 +27,7 @@ function M:new(args) client = args.client, }) - o.runner = JavaDapRunner:new({ - reporter = TestReport:new(), - }) + o.runner = args.runner setmetatable(o, self) self.__index = self @@ -60,9 +56,14 @@ end ---Runs the test class in the given buffer ---@param buffer integer ---@param config JavaCoreDapLauncherConfigOverridable -function M:run_class_by_buffer(buffer, config) + +---comment +---@param buffer number +---@param report java_test.JUnitTestReport +---@param config? JavaCoreDapLauncherConfigOverridable config to override the default values in test launcher config +function M:run_class_by_buffer(buffer, report, config) local tests = self:get_test_class_by_buffer(buffer) - self:run_test(tests, config) + self:run_test(tests, report, config) end ---Returns test classes in the given buffer @@ -78,9 +79,9 @@ end ---Run the given test ---@param tests java_core.TestDetails[] +---@param report java_test.JUnitTestReport ---@param config? JavaCoreDapLauncherConfigOverridable config to override the default values in test launcher config -function M:run_test(tests, config) - ---@type JavaCoreTestJunitLaunchArguments +function M:run_test(tests, report, config) local launch_args = self.test_client:resolve_junit_launch_arguments( data_adapters.get_junit_launch_argument_params(tests) ) @@ -99,7 +100,7 @@ function M:run_test(tests, config) dap_launcher_config = vim.tbl_deep_extend('force', dap_launcher_config, config or {}) - self.runner:run_by_config(dap_launcher_config) + self.runner:run_by_config(dap_launcher_config, report) end return M diff --git a/lua/java-core/dap/adapters.lua b/lua/java-core/dap/adapters.lua index 801f7f3..47c17f7 100644 --- a/lua/java-core/dap/adapters.lua +++ b/lua/java-core/dap/adapters.lua @@ -4,7 +4,7 @@ local M = {} ---@param main JavaDebugResolveMainClassRecord ---@param classpath string[][] ---@param java_exec string ----@return JavaCoreDapLauncherConfig +---@return java_core.DapLauncherConfig function M.get_dap_config(main, classpath, java_exec) local project_name = main.projectName local main_class = main.mainClass diff --git a/lua/java-core/dap/init.lua b/lua/java-core/dap/init.lua index 0d91bb6..5a8e910 100644 --- a/lua/java-core/dap/init.lua +++ b/lua/java-core/dap/init.lua @@ -44,7 +44,7 @@ function M:get_dap_adapter() end ---Returns the dap configuration for the current project ----@return JavaCoreDapLauncherConfig[] # dap configuration details +---@return java_core.DapLauncherConfig[] # dap configuration details function M:get_dap_config() log.info('creating dap configuration for java') @@ -60,7 +60,7 @@ end ---Returns the dap config for the given main class ---@param main JavaDebugResolveMainClassRecord ----@return JavaCoreDapLauncherConfig # dap launch configuration record +---@return java_core.DapLauncherConfig # dap launch configuration record function M:get_dap_config_record(main) local classpaths = self.java_debug:resolve_classpath(main.mainClass, main.projectName) @@ -92,7 +92,7 @@ return M ---@field preLaunchTask? string ---@field postDebugTask? string ----@class JavaCoreDapLauncherConfig +---@class java_core.DapLauncherConfig ---@field name string ---@field type string ---@field request string diff --git a/lua/java-core/dap/runner.lua b/lua/java-core/dap/runner.lua index 3745ca9..cdade71 100644 --- a/lua/java-core/dap/runner.lua +++ b/lua/java-core/dap/runner.lua @@ -1,15 +1,12 @@ local log = require('java-core.utils.log') ----@class JavaCoreDapRunner ----@field reporter JavaCoreDapRunReport +---@class java_core.DapRunner ---@field private server uv_tcp_t local M = {} ----@param args { reporter: JavaCoreDapRunReport } ----@return JavaCoreDapRunner -function M:new(args) +---@return java_core.DapRunner +function M:new() local o = { - reporter = args.reporter, server = nil, } @@ -19,13 +16,14 @@ function M:new(args) end ---Dap run with given config ----@param config JavaCoreDapLauncherConfig -function M:run_by_config(config) +---@param config java_core.DapLauncherConfig +---@param report java_test.JUnitTestReport +function M:run_by_config(config, report) log.debug('running dap with config: ', config) require('dap').run(config --[[@as Configuration]], { before = function(conf) - return self:before(conf) + return self:before(conf, report) end, after = function() @@ -36,9 +34,10 @@ end ---Runs before the dap run ---@private ----@param conf JavaCoreDapLauncherConfig ----@return JavaCoreDapLauncherConfig -function M:before(conf) +---@param conf java_core.DapLauncherConfig +---@param report java_test.JUnitTestReport +---@return java_core.DapLauncherConfig +function M:before(conf, report) log.debug('running "before" callback') self.server = assert(vim.loop.new_tcp(), 'uv.new_tcp() must return handle') @@ -48,7 +47,7 @@ function M:before(conf) local sock = assert(vim.loop.new_tcp(), 'uv.new_tcp must return handle') self.server:accept(sock) - local success = sock:read_start(self.reporter:get_stream_reader(sock)) + local success = sock:read_start(report:get_stream_reader(sock)) assert(success == 0, 'failed to listen to reader') end) @@ -72,5 +71,5 @@ end return M ----@class JavaCoreDapRunReport ----@field get_stream_reader fun(self: JavaCoreDapRunReport, conn: uv_tcp_t): fun(err: string|nil, buffer: string|nil) +---@class java_core.DapRunReport +---@field get_stream_reader fun(self: java_core.DapRunReport, conn: uv_tcp_t): fun(err: string|nil, buffer: string|nil) diff --git a/lua/java-core/dap/test-report.lua b/lua/java-core/dap/test-report.lua deleted file mode 100644 index 37ce06f..0000000 --- a/lua/java-core/dap/test-report.lua +++ /dev/null @@ -1,50 +0,0 @@ -local log = require('java-core.utils.log') - ----@class JavaCoreDapTestReport: JavaCoreDapRunReport ----@field private conn uv_tcp_t -local M = {} - -function M:new() - local o = {} - setmetatable(o, self) - self.__index = self - return o -end - ----Returns a stream reader function ----@param conn uv_tcp_t ----@return fun(err: string, buffer: string) -function M:get_stream_reader(conn) - self.conn = conn - - return vim.schedule_wrap(function(err, buffer) - if err then - self:on_error(err) - self:on_close() - - self.conn:close() - return - end - - if buffer then - self:on_update(buffer) - else - self:on_close() - self.conn:close() - end - end) -end - -function M:on_update(buffer) - vim.print(buffer) -end - -function M:on_close() - vim.print('closing') -end - -function M:on_error(err) - log.error('Error while running test', err) -end - -return M diff --git a/lua/java-core/utils/class.lua b/lua/java-core/utils/class.lua new file mode 100644 index 0000000..b5a9061 --- /dev/null +++ b/lua/java-core/utils/class.lua @@ -0,0 +1,290 @@ +--- Provides a reuseable and convenient framework for creating classes in Lua. +-- Two possible notations: +-- +-- B = class(A) +-- class.B(A) +-- +-- The latter form creates a named class within the current environment. Note +-- that this implicitly brings in `pl.utils` as a dependency. +-- +-- See the Guide for further @{01-introduction.md.Simplifying_Object_Oriented_Programming_in_Lua|discussion} +-- @module pl.class + +local error, getmetatable, io, pairs, rawget, rawset, setmetatable, tostring, type = + _G.error, + _G.getmetatable, + _G.io, + _G.pairs, + _G.rawget, + _G.rawset, + _G.setmetatable, + _G.tostring, + _G.type +local compat + +-- this trickery is necessary to prevent the inheritance of 'super' and +-- the resulting recursive call problems. +local function call_ctor(c, obj, ...) + local init = rawget(c, '_init') + local parent_with_init = rawget(c, '_parent_with_init') + + if parent_with_init then + if not init then -- inheriting an init + init = rawget(parent_with_init, '_init') + parent_with_init = rawget(parent_with_init, '_parent_with_init') + end + if parent_with_init then -- super() points to one above whereever _init came from + rawset(obj, 'super', function(obj, ...) + call_ctor(parent_with_init, obj, ...) + end) + end + else + -- Without this, calling super() where none exists will sometimes loop and stack overflow + rawset(obj, 'super', nil) + end + + local res = init(obj, ...) + if parent_with_init then -- If this execution of call_ctor set a super, unset it + rawset(obj, 'super', nil) + end + + return res +end + +--- initializes an __instance__ upon creation. +-- @function class:_init +-- @param ... parameters passed to the constructor +-- @usage local Cat = class() +-- function Cat:_init(name) +-- --self:super(name) -- call the ancestor initializer if needed +-- self.name = name +-- end +-- +-- local pussycat = Cat("pussycat") +-- print(pussycat.name) --> pussycat + +--- checks whether an __instance__ is derived from some class. +-- Works the other way around as `class_of`. It has two ways of using; +-- 1) call with a class to check against, 2) call without params. +-- @function instance:is_a +-- @param some_class class to check against, or `nil` to return the class +-- @return `true` if `instance` is derived from `some_class`, or if `some_class == nil` then +-- it returns the class table of the instance +-- @usage local pussycat = Lion() -- assuming Lion derives from Cat +-- if pussycat:is_a(Cat) then +-- -- it's true, it is a Lion, but also a Cat +-- end +-- +-- if pussycat:is_a() == Lion then +-- -- It's true +-- end +local function is_a(self, klass) + if klass == nil then + -- no class provided, so return the class this instance is derived from + return getmetatable(self) + end + local m = getmetatable(self) + if not m then + return false + end --*can't be an object! + while m do + if m == klass then + return true + end + m = rawget(m, '_base') + end + return false +end + +--- checks whether an __instance__ is derived from some class. +-- Works the other way around as `is_a`. +-- @function some_class:class_of +-- @param some_instance instance to check against +-- @return `true` if `some_instance` is derived from `some_class` +-- @usage local pussycat = Lion() -- assuming Lion derives from Cat +-- if Cat:class_of(pussycat) then +-- -- it's true +-- end +local function class_of(klass, obj) + if type(klass) ~= 'table' or not rawget(klass, 'is_a') then + return false + end + return klass.is_a(obj, klass) +end + +--- cast an object to another class. +-- It is not clever (or safe!) so use carefully. +-- @param some_instance the object to be changed +-- @function some_class:cast +local function cast(klass, obj) + return setmetatable(obj, klass) +end + +local function _class_tostring(obj) + local mt = obj._class + local name = rawget(mt, '_name') + setmetatable(obj, nil) + local str = tostring(obj) + setmetatable(obj, mt) + if name then + str = name .. str:gsub('table', '') + end + return str +end + +local function tupdate(td, ts, dont_override) + for k, v in pairs(ts) do + if not dont_override or td[k] == nil then + td[k] = v + end + end +end + +local function _class(base, c_arg, c) + -- the class `c` will be the metatable for all its objects, + -- and they will look up their methods in it. + local mt = {} -- a metatable for the class to support __call and _handler + -- can define class by passing it a plain table of methods + local plain = type(base) == 'table' and not getmetatable(base) + if plain then + c = base + base = c._base + else + c = c or {} + end + + if type(base) == 'table' then + -- our new class is a shallow copy of the base class! + -- but be careful not to wipe out any methods we have been given at this point! + tupdate(c, base, plain) + c._base = base + -- inherit the 'not found' handler, if present + if rawget(c, '_handler') then + mt.__index = c._handler + end + elseif base ~= nil then + error('must derive from a table type', 3) + end + + c.__index = c + setmetatable(c, mt) + if not plain then + if base and rawget(base, '_init') then + c._parent_with_init = base + end -- For super and inherited init + c._init = nil + end + + if base and rawget(base, '_class_init') then + base._class_init(c, c_arg) + end + + -- expose a ctor which can be called by () + mt.__call = function(class_tbl, ...) + local obj + if rawget(c, '_create') then + obj = c._create(...) + end + if not obj then + obj = {} + end + setmetatable(obj, c) + + if rawget(c, '_init') or rawget(c, '_parent_with_init') then -- constructor exists + local res = call_ctor(c, obj, ...) + if res then -- _if_ a ctor returns a value, it becomes the object... + obj = res + setmetatable(obj, c) + end + end + + if base and rawget(base, '_post_init') then + base._post_init(obj) + end + + return obj + end + -- Call Class.catch to set a handler for methods/properties not found in the class! + c.catch = function(self, handler) + if type(self) == 'function' then + -- called using . instead of : + handler = self + end + c._handler = handler + mt.__index = handler + end + c.is_a = is_a + c.class_of = class_of + c.cast = cast + c._class = c + + if not rawget(c, '__tostring') then + c.__tostring = _class_tostring + end + + return c +end + +--- create a new class, derived from a given base class. +-- Supporting two class creation syntaxes: +-- either `Name = class(base)` or `class.Name(base)`. +-- The first form returns the class directly and does not set its `_name`. +-- The second form creates a variable `Name` in the current environment set +-- to the class, and also sets `_name`. +-- @function class +-- @param base optional base class +-- @param c_arg optional parameter to class constructor +-- @param c optional table to be used as class +local class +class = setmetatable({}, { + __call = function(fun, ...) + return _class(...) + end, + __index = function(tbl, key) + if key == 'class' then + io.stderr:write( + 'require("pl.class").class is deprecated. Use require("pl.class")\n' + ) + return class + end + compat = compat or require('pl.compat') + local env = compat.getfenv(2) + return function(...) + local c = _class(...) + c._name = key + rawset(env, key, c) + return c + end + end, +}) + +class.properties = class() + +function class.properties._class_init(klass) + klass.__index = function(t, key) + -- normal class lookup! + local v = klass[key] + if v then + return v + end + -- is it a getter? + v = rawget(klass, 'get_' .. key) + if v then + return v(t) + end + -- is it a field? + return rawget(t, '_' .. key) + end + klass.__newindex = function(t, key, value) + -- if there's a setter, use that, otherwise directly set table + local p = 'set_' .. key + local setter = klass[p] + if setter then + setter(t, value) + else + rawset(t, key, value) + end + end +end + +return class