diff --git a/lua/crates/diagnostic.lua b/lua/crates/diagnostic.lua index 42fba251..462a7319 100644 --- a/lua/crates/diagnostic.lua +++ b/lua/crates/diagnostic.lua @@ -231,7 +231,9 @@ function M.process_api_crate(crate, api_crate) } local diagnostics = {} - if crate.path then + if crate.workspace then + info.dep_kind = "workspace" + elseif crate.path then info.dep_kind = "path" elseif crate.git then info.dep_kind = "git" diff --git a/lua/crates/popup/features.lua b/lua/crates/popup/features.lua index 2b0ee61b..cf301b9e 100644 --- a/lua/crates/popup/features.lua +++ b/lua/crates/popup/features.lua @@ -107,7 +107,7 @@ local function toggle_feature(ctx, line) local text = vim.api.nvim_buf_get_lines(ctx.buf, line_nr, line_nr + 1, false)[1] text = toml.trim_comments(text) - local crate = toml.Crate.new(toml.parse_crate(text, line_nr)) + local crate = toml.Crate.new(toml.parse_inline_crate(text, line_nr)) ctx.crate.syntax = crate.syntax ctx.crate.vers = crate.vers ctx.crate.feat = crate.feat diff --git a/lua/crates/popup/versions.lua b/lua/crates/popup/versions.lua index e8b61241..57b6d0a3 100644 --- a/lua/crates/popup/versions.lua +++ b/lua/crates/popup/versions.lua @@ -49,7 +49,7 @@ local function select_version(ctx, line, alt) local text = vim.api.nvim_buf_get_lines(ctx.buf, line_nr, line_nr + 1, false)[1] text = toml.trim_comments(text) - local c = toml.parse_crate(text, line_nr) + local c = toml.parse_inline_crate(text, line_nr) if c and c.vers then crate.vers = crate.vers or c.vers crate.vers.line = line_nr diff --git a/lua/crates/toml.lua b/lua/crates/toml.lua index dd67023c..b19e742a 100644 --- a/lua/crates/toml.lua +++ b/lua/crates/toml.lua @@ -1,4 +1,13 @@ -local M = {Section = {}, Crate = {Vers = {}, Path = {}, Git = {}, Pkg = {}, Def = {}, Feat = {}, }, Feature = {}, Quotes = {}, } +local M = {Section = {}, Crate = {Vers = {}, Path = {}, Git = {}, Pkg = {}, Workspace = {}, Def = {}, Feat = {}, }, Feature = {}, Quotes = {}, } + + + + + + + + + @@ -111,25 +120,6 @@ local types = require("crates.types") local Range = types.Range local Requirement = types.Requirement -local function inline_table_bool_pattern(name) - return "^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*" .. name .. "%s*=%s*()([^%s,}]*)()%s*()[,]?.*[}]?%s*$" -end - -local function inline_table_str_pattern(name) - return [[^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*]] .. name .. [[%s*=%s*(["'])()([^"']*)()(["']?)%s*()[,]?.*[}]?%s*$]] -end - -local function inline_table_str_array_pattern(name) - return "^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*" .. name .. "%s*=%s*%[()([^%]]*)()[%]]?%s*()[,]?.*[}]?%s*$" -end - -local INLINE_TABLE_VERS_PATTERN = inline_table_str_pattern("version") -local INLINE_TABLE_PATH_PATTERN = inline_table_str_pattern("path") -local INLINE_TABLE_GIT_PATTERN = inline_table_str_pattern("git") -local INLINE_TABLE_PKG_PATTERN = inline_table_str_pattern("package") -local INLINE_TABLE_FEAT_PATTERN = inline_table_str_array_pattern("features") -local INLINE_TABLE_DEF_PATTERN = inline_table_bool_pattern("default[_-]features") - function M.parse_crate_features(text) local feats = {} for fds, qs, fs, f, fe, qe, fde, c in text:gmatch([[[,]?()%s*(["'])()([^,"']*)()(["']?)%s*()([,]?)]]) do @@ -163,6 +153,9 @@ function Crate.new(obj) if obj.def then obj.def.enabled = obj.def.text ~= "false" end + if obj.workspace then + obj.workspace.enabled = obj.workspace.text ~= "false" + end return setmetatable(obj, { __index = Crate }) end @@ -197,6 +190,10 @@ function Crate:is_def_enabled() return not self.def or self.def.enabled end +function Crate:is_workspace() + return not self.workspace or self.workspace.enabled +end + function Crate:package() return self.pkg and self.pkg.text or self.explicit_name end @@ -271,6 +268,7 @@ function M.parse_section(text, start) return nil end + local function parse_crate_table_str(line, line_nr, pattern) local quote_s, str_s, text, str_e, quote_e = line:match(pattern) if text then @@ -286,6 +284,20 @@ local function parse_crate_table_str(line, line_nr, pattern) return nil end +local function parse_crate_table_bool(line, line_nr, pattern) + local bool_s, text, bool_e = line:match(pattern) + if text then + return { + text = text, + line = line_nr, + col = Range.new(bool_s - 1, bool_e - 1), + decl_col = Range.new(0, line:len()), + } + end + + return nil +end + function M.parse_crate_table_vers(line, line_nr) local pat = [[^%s*version%s*=%s*(["'])()([^"']*)()(["']?)%s*$]] return parse_crate_table_str(line, line_nr, pat) @@ -306,6 +318,16 @@ function M.parse_crate_table_pkg(line, line_nr) return parse_crate_table_str(line, line_nr, pat) end +function M.parse_crate_table_def(line, line_nr) + local pat = "^%s*default[_-]features%s*=%s*()([^%s]*)()%s*$" + return parse_crate_table_bool(line, line_nr, pat) +end + +function M.parse_crate_table_workspace(line, line_nr) + local pat = "^%s*workspace%s*=%s*()([^%s]*)()%s*$" + return parse_crate_table_bool(line, line_nr, pat) +end + function M.parse_crate_table_feat(line, line_nr) local array_s, text, array_e = line:match("%s*features%s*=%s*%[()([^%]]*)()[%]]?%s*$") if text then @@ -320,20 +342,27 @@ function M.parse_crate_table_feat(line, line_nr) return nil end -function M.parse_crate_table_def(line, line_nr) - local bool_s, text, bool_e = line:match("^%s*default[_-]features%s*=%s*()([^%s]*)()%s*$") - if text then - return { - text = text, - line = line_nr, - col = Range.new(bool_s - 1, bool_e - 1), - decl_col = Range.new(0, line:len()), - } - end - return nil +local function inline_table_bool_pattern(name) + return "^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*" .. name .. "%s*=%s*()([^%s,}]*)()%s*()[,]?.*[}]?%s*$" +end + +local function inline_table_str_pattern(name) + return [[^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*]] .. name .. [[%s*=%s*(["'])()([^"']*)()(["']?)%s*()[,]?.*[}]?%s*$]] +end + +local function inline_table_str_array_pattern(name) + return "^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*" .. name .. "%s*=%s*%[()([^%]]*)()[%]]?%s*()[,]?.*[}]?%s*$" end +local INLINE_TABLE_VERS_PATTERN = inline_table_str_pattern("version") +local INLINE_TABLE_PATH_PATTERN = inline_table_str_pattern("path") +local INLINE_TABLE_GIT_PATTERN = inline_table_str_pattern("git") +local INLINE_TABLE_PKG_PATTERN = inline_table_str_pattern("package") +local INLINE_TABLE_FEAT_PATTERN = inline_table_str_array_pattern("features") +local INLINE_TABLE_DEF_PATTERN = inline_table_bool_pattern("default[_-]features") +local INLINE_TABLE_WORKSPACE_PATTERN = inline_table_bool_pattern("workspace") + local function parse_inline_table_str(crate, line, line_nr, entry, pattern) local name_s, name, name_e, decl_s, quote_s, str_s, text, str_e, quote_e, decl_e = line:match(pattern) if name then @@ -351,7 +380,23 @@ local function parse_inline_table_str(crate, line, line_nr, entry, pattern) end end -function M.parse_crate(line, line_nr) +local function parse_inline_table_bool(crate, line, line_nr, entry, pattern) + local name_s, name, name_e, decl_s, str_s, text, str_e, decl_e = line:match(pattern) + if name then + crate.explicit_name = name + crate.explicit_name_col = Range.new(name_s - 1, name_e - 1) + do + (crate)[entry] = { + text = text, + line = line_nr, + col = Range.new(str_s - 1, str_e - 1), + decl_col = Range.new(decl_s - 1, decl_e - 1), + } + end + end +end + +function M.parse_inline_crate(line, line_nr) do local name_s, name, name_e, quote_s, str_s, text, str_e, quote_e = line:match([[^%s*()([^%s]+)()%s*=%s*(["'])()([^"']*)()(["']?)%s*$]]) @@ -383,6 +428,9 @@ function M.parse_crate(line, line_nr) parse_inline_table_str(crate, line, line_nr, "git", INLINE_TABLE_GIT_PATTERN) parse_inline_table_str(crate, line, line_nr, "pkg", INLINE_TABLE_PKG_PATTERN) + parse_inline_table_bool(crate, line, line_nr, "def", INLINE_TABLE_DEF_PATTERN) + parse_inline_table_bool(crate, line, line_nr, "workspace", INLINE_TABLE_WORKSPACE_PATTERN) + do local name_s, name, name_e, decl_s, array_s, text, array_e, decl_e = line:match(INLINE_TABLE_FEAT_PATTERN) if name then @@ -397,20 +445,6 @@ function M.parse_crate(line, line_nr) end end - do - local name_s, name, name_e, decl_s, bool_s, text, bool_e, decl_e = line:match(INLINE_TABLE_DEF_PATTERN) - if name then - crate.explicit_name = name - crate.explicit_name_col = Range.new(name_s - 1, name_e - 1) - crate.def = { - text = text, - line = line_nr, - col = Range.new(bool_s - 1, bool_e - 1), - decl_col = Range.new(decl_s - 1, decl_e - 1), - } - end - end - if crate.explicit_name then return crate end @@ -493,6 +527,12 @@ function M.parse_crates(buf) dep_section_crate.pkg = pkg end + local workspace = M.parse_crate_table_workspace(line, line_nr) + if workspace then + dep_section_crate = dep_section_crate or empty_crate + dep_section_crate.workspace = workspace + end + local feat = M.parse_crate_table_feat(line, line_nr) if feat then dep_section_crate = dep_section_crate or empty_crate @@ -505,7 +545,7 @@ function M.parse_crates(buf) dep_section_crate.def = def end elseif dep_section then - local crate = M.parse_crate(line, line_nr) + local crate = M.parse_inline_crate(line, line_nr) if crate then crate.section = dep_section table.insert(crates, Crate.new(crate)) diff --git a/lua/crates/types.lua b/lua/crates/types.lua index c7736461..bb05677a 100644 --- a/lua/crates/types.lua +++ b/lua/crates/types.lua @@ -138,6 +138,7 @@ local M = {CrateInfo = {}, Diagnostic = {}, Crate = {}, Version = {}, Features = + local Diagnostic = M.Diagnostic diff --git a/teal/crates/diagnostic.tl b/teal/crates/diagnostic.tl index 1830eeee..d474abec 100644 --- a/teal/crates/diagnostic.tl +++ b/teal/crates/diagnostic.tl @@ -231,7 +231,9 @@ function M.process_api_crate(crate: toml.Crate, api_crate: Crate): CrateInfo, {D } local diagnostics = {} - if crate.path then + if crate.workspace then + info.dep_kind = "workspace" + elseif crate.path then info.dep_kind = "path" elseif crate.git then info.dep_kind = "git" diff --git a/teal/crates/popup/features.tl b/teal/crates/popup/features.tl index 11579943..ddb33009 100644 --- a/teal/crates/popup/features.tl +++ b/teal/crates/popup/features.tl @@ -107,7 +107,7 @@ local function toggle_feature(ctx: FeatureContext, line: integer) local text = vim.api.nvim_buf_get_lines(ctx.buf, line_nr, line_nr + 1, false)[1] text = toml.trim_comments(text) - local crate = toml.Crate.new(toml.parse_crate(text, line_nr)) + local crate = toml.Crate.new(toml.parse_inline_crate(text, line_nr)) ctx.crate.syntax = crate.syntax ctx.crate.vers = crate.vers ctx.crate.feat = crate.feat diff --git a/teal/crates/popup/versions.tl b/teal/crates/popup/versions.tl index 24295990..3543795a 100644 --- a/teal/crates/popup/versions.tl +++ b/teal/crates/popup/versions.tl @@ -49,7 +49,7 @@ local function select_version(ctx: VersContext, line: integer, alt: boolean|nil) local text = vim.api.nvim_buf_get_lines(ctx.buf, line_nr, line_nr + 1, false)[1] text = toml.trim_comments(text) - local c = toml.parse_crate(text, line_nr) + local c = toml.parse_inline_crate(text, line_nr) if c and c.vers then crate.vers = crate.vers or c.vers crate.vers.line = line_nr diff --git a/teal/crates/toml.tl b/teal/crates/toml.tl index fe922af0..4b7c3363 100644 --- a/teal/crates/toml.tl +++ b/teal/crates/toml.tl @@ -28,6 +28,7 @@ local record M path: Path git: Git pkg: Pkg + workspace: Workspace def: Def feat: Feat section: Section @@ -72,6 +73,14 @@ local record M quote: Quotes end + record Workspace + enabled: boolean + text: string + line: integer -- 0-indexed + col: Range + decl_col: Range + end + record Def enabled: boolean text: string @@ -111,25 +120,6 @@ local types = require("crates.types") local Range = types.Range local Requirement = types.Requirement -local function inline_table_bool_pattern(name: string): string - return "^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*" .. name .. "%s*=%s*()([^%s,}]*)()%s*()[,]?.*[}]?%s*$" -end - -local function inline_table_str_pattern(name: string): string - return [[^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*]] .. name .. [[%s*=%s*(["'])()([^"']*)()(["']?)%s*()[,]?.*[}]?%s*$]] -end - -local function inline_table_str_array_pattern(name: string): string - return "^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*" .. name .. "%s*=%s*%[()([^%]]*)()[%]]?%s*()[,]?.*[}]?%s*$" -end - -local INLINE_TABLE_VERS_PATTERN = inline_table_str_pattern("version") -local INLINE_TABLE_PATH_PATTERN = inline_table_str_pattern("path") -local INLINE_TABLE_GIT_PATTERN = inline_table_str_pattern("git") -local INLINE_TABLE_PKG_PATTERN = inline_table_str_pattern("package") -local INLINE_TABLE_FEAT_PATTERN = inline_table_str_array_pattern("features") -local INLINE_TABLE_DEF_PATTERN = inline_table_bool_pattern("default[_-]features") - function M.parse_crate_features(text: string): {Feature} local feats: {Feature} = {} for fds, qs, fs, f, fe, qe, fde, c in text:gmatch([[[,]?()%s*(["'])()([^,"']*)()(["']?)%s*()([,]?)]]) do @@ -163,6 +153,9 @@ function Crate.new(obj: Crate): Crate if obj.def then obj.def.enabled = obj.def.text ~= "false" end + if obj.workspace then + obj.workspace.enabled = obj.workspace.text ~= "false" + end return setmetatable(obj, { __index = Crate }) end @@ -197,6 +190,10 @@ function Crate:is_def_enabled(): boolean return not self.def or self.def.enabled end +function Crate:is_workspace(): boolean + return not self.workspace or self.workspace.enabled +end + function Crate:package(): string return self.pkg and self.pkg.text or self.explicit_name end @@ -271,6 +268,7 @@ function M.parse_section(text: string, start: integer): Section return nil end + local function parse_crate_table_str(line: string, line_nr: integer, pattern: string): table|nil local quote_s, str_s, text, str_e, quote_e = line:match(pattern) if text then @@ -286,6 +284,20 @@ local function parse_crate_table_str(line: string, line_nr: integer, pattern: st return nil end +local function parse_crate_table_bool(line: string, line_nr: integer, pattern: string): table|nil + local bool_s, text, bool_e = line:match(pattern) + if text then + return { + text = text, + line = line_nr, + col = Range.new(bool_s as integer - 1, bool_e as integer - 1), + decl_col = Range.new(0, line:len()), + } + end + + return nil +end + function M.parse_crate_table_vers(line: string, line_nr: integer): Crate.Vers local pat = [[^%s*version%s*=%s*(["'])()([^"']*)()(["']?)%s*$]] return parse_crate_table_str(line, line_nr, pat) as Crate.Vers @@ -306,6 +318,16 @@ function M.parse_crate_table_pkg(line: string, line_nr: integer): Crate.Pkg return parse_crate_table_str(line, line_nr, pat) as Crate.Pkg end +function M.parse_crate_table_def(line: string, line_nr: integer): Crate.Def + local pat = "^%s*default[_-]features%s*=%s*()([^%s]*)()%s*$" + return parse_crate_table_bool(line, line_nr, pat) as Crate.Def +end + +function M.parse_crate_table_workspace(line: string, line_nr: integer): Crate.Workspace + local pat = "^%s*workspace%s*=%s*()([^%s]*)()%s*$" + return parse_crate_table_bool(line, line_nr, pat) as Crate.Workspace +end + function M.parse_crate_table_feat(line: string, line_nr: integer): Crate.Feat local array_s, text, array_e = line:match("%s*features%s*=%s*%[()([^%]]*)()[%]]?%s*$") if text then @@ -320,20 +342,27 @@ function M.parse_crate_table_feat(line: string, line_nr: integer): Crate.Feat return nil end -function M.parse_crate_table_def(line: string, line_nr: integer): Crate.Def - local bool_s, text, bool_e = line:match("^%s*default[_-]features%s*=%s*()([^%s]*)()%s*$") - if text then - return { - text = text, - line = line_nr, - col = Range.new(bool_s as integer - 1, bool_e as integer - 1), - decl_col = Range.new(0, line:len()), - } - end - return nil +local function inline_table_bool_pattern(name: string): string + return "^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*" .. name .. "%s*=%s*()([^%s,}]*)()%s*()[,]?.*[}]?%s*$" +end + +local function inline_table_str_pattern(name: string): string + return [[^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*]] .. name .. [[%s*=%s*(["'])()([^"']*)()(["']?)%s*()[,]?.*[}]?%s*$]] +end + +local function inline_table_str_array_pattern(name: string): string + return "^%s*()([^%s]+)()%s*=%s*{.-[,]?()%s*" .. name .. "%s*=%s*%[()([^%]]*)()[%]]?%s*()[,]?.*[}]?%s*$" end +local INLINE_TABLE_VERS_PATTERN = inline_table_str_pattern("version") +local INLINE_TABLE_PATH_PATTERN = inline_table_str_pattern("path") +local INLINE_TABLE_GIT_PATTERN = inline_table_str_pattern("git") +local INLINE_TABLE_PKG_PATTERN = inline_table_str_pattern("package") +local INLINE_TABLE_FEAT_PATTERN = inline_table_str_array_pattern("features") +local INLINE_TABLE_DEF_PATTERN = inline_table_bool_pattern("default[_-]features") +local INLINE_TABLE_WORKSPACE_PATTERN = inline_table_bool_pattern("workspace") + local function parse_inline_table_str(crate: Crate, line: string, line_nr: integer, entry: string, pattern: string) local name_s, name, name_e, decl_s, quote_s, str_s, text, str_e, quote_e, decl_e = line:match(pattern) if name then @@ -351,7 +380,23 @@ local function parse_inline_table_str(crate: Crate, line: string, line_nr: integ end end -function M.parse_crate(line: string, line_nr: integer): Crate +local function parse_inline_table_bool(crate: Crate, line: string, line_nr: integer, entry: string, pattern: string) + local name_s, name, name_e, decl_s, str_s, text, str_e, decl_e = line:match(pattern) + if name then + crate.explicit_name = name + crate.explicit_name_col = Range.new(name_s as integer - 1, name_e as integer - 1) + do + (crate as {string:any})[entry] = { + text = text, + line = line_nr, + col = Range.new(str_s as integer - 1, str_e as integer - 1), + decl_col = Range.new(decl_s as integer - 1, decl_e as integer - 1), + } + end + end +end + +function M.parse_inline_crate(line: string, line_nr: integer): Crate -- plain version do local name_s, name, name_e, quote_s, str_s, text, str_e, quote_e = line:match([[^%s*()([^%s]+)()%s*=%s*(["'])()([^"']*)()(["']?)%s*$]]) @@ -383,6 +428,9 @@ function M.parse_crate(line: string, line_nr: integer): Crate parse_inline_table_str(crate, line, line_nr, "git", INLINE_TABLE_GIT_PATTERN) parse_inline_table_str(crate, line, line_nr, "pkg", INLINE_TABLE_PKG_PATTERN) + parse_inline_table_bool(crate, line, line_nr, "def", INLINE_TABLE_DEF_PATTERN) + parse_inline_table_bool(crate, line, line_nr, "workspace", INLINE_TABLE_WORKSPACE_PATTERN) + do local name_s, name, name_e, decl_s, array_s, text, array_e, decl_e = line:match(INLINE_TABLE_FEAT_PATTERN) if name then @@ -397,20 +445,6 @@ function M.parse_crate(line: string, line_nr: integer): Crate end end - do - local name_s, name, name_e, decl_s, bool_s, text, bool_e, decl_e = line:match(INLINE_TABLE_DEF_PATTERN) - if name then - crate.explicit_name = name - crate.explicit_name_col = Range.new(name_s as integer - 1, name_e as integer - 1) - crate.def = { - text = text, - line = line_nr, - col = Range.new(bool_s as integer - 1, bool_e as integer - 1), - decl_col = Range.new(decl_s as integer - 1, decl_e as integer - 1), - } - end - end - if crate.explicit_name then return crate end @@ -493,6 +527,12 @@ function M.parse_crates(buf: integer): {Section}, {Crate} dep_section_crate.pkg = pkg end + local workspace = M.parse_crate_table_workspace(line, line_nr) + if workspace then + dep_section_crate = dep_section_crate or empty_crate + dep_section_crate.workspace = workspace + end + local feat = M.parse_crate_table_feat(line, line_nr) if feat then dep_section_crate = dep_section_crate or empty_crate @@ -505,7 +545,7 @@ function M.parse_crates(buf: integer): {Section}, {Crate} dep_section_crate.def = def end elseif dep_section then - local crate = M.parse_crate(line, line_nr) + local crate = M.parse_inline_crate(line, line_nr) if crate then crate.section = dep_section table.insert(crates, Crate.new(crate)) diff --git a/teal/crates/types.tl b/teal/crates/types.tl index 04f259fb..cb9c0bc4 100644 --- a/teal/crates/types.tl +++ b/teal/crates/types.tl @@ -20,6 +20,7 @@ local record M "registry" "path" "git" + "workspace" end record Diagnostic