feat: auto-uppercase keywords and auto-semicolon for END_* keywords

- Auto-uppercase SCL keywords (IF, CASE, FOR, NOT, AND, etc.) when typing space
- Auto-add semicolon after END_* keywords (END_IF, END_FOR, etc.) when:
  - Leaving insert mode on a line ending with END_*
  - Pressing Enter after typing END_*
- Added complete keyword list including TYPE, STRUCT, DATA_BLOCK, etc.
- Works for .scl, .udt, and .db files
This commit is contained in:
2026-02-21 01:50:39 +01:00
parent 06e36a0b4b
commit 97176508d3
+130 -3
View File
@@ -33,9 +33,24 @@ local keywords = {
"ORGANIZATION_BLOCK", "END_ORGANIZATION_BLOCK", "ORGANIZATION_BLOCK", "END_ORGANIZATION_BLOCK",
"FUNCTION_BLOCK", "END_FUNCTION_BLOCK", "FUNCTION_BLOCK", "END_FUNCTION_BLOCK",
"FUNCTION", "END_FUNCTION", "FUNCTION", "END_FUNCTION",
"RETURN", "RETURN", "TYPE", "END_TYPE", "STRUCT", "END_STRUCT",
"DATA_BLOCK", "END_DATA_BLOCK",
"CONSTANT", "RETAIN", "NON_RETAIN",
} }
-- Keywords that need semicolon after them (when at end of statement)
local end_keywords = {
"END_IF", "END_FOR", "END_WHILE", "END_REPEAT", "END_CASE",
"END_VAR", "END_FUNCTION", "END_FUNCTION_BLOCK", "END_ORGANIZATION_BLOCK",
"END_TYPE", "END_STRUCT", "END_DATA_BLOCK", "END_REGION",
}
-- Map lowercase to uppercase for auto-conversion
local keyword_map = {}
for _, kw in ipairs(keywords) do
keyword_map[kw:lower()] = kw
end
-- Check if word is a keyword -- Check if word is a keyword
local function is_keyword(word) local function is_keyword(word)
for _, kw in ipairs(keywords) do for _, kw in ipairs(keywords) do
@@ -393,6 +408,74 @@ local function on_dot_typed()
end end
end end
-- Auto-uppercase keywords when typing space
local function auto_uppercase_keyword()
-- Only in SCL files
local ft = vim.bo.filetype
if ft ~= "scl" and ft ~= "udt" and ft ~= "db" then
return
end
local bufnr = vim.api.nvim_get_current_buf()
local row = vim.api.nvim_win_get_cursor(0)[1]
local col = vim.api.nvim_win_get_cursor(0)[2]
local line = vim.api.nvim_buf_get_lines(bufnr, row - 1, row, false)[1]
-- Check if we just typed a space
if col < 1 or line:sub(col, col) ~= " " then
return
end
-- Find word before the space
local word_end = col - 1
local word_start = word_end
while word_start > 0 and line:sub(word_start, word_start):match("[%w_]") do
word_start = word_start - 1
end
word_start = word_start + 1
if word_start > word_end then
return
end
local word = line:sub(word_start, word_end)
local lower_word = word:lower()
-- Check if it's a keyword that needs uppercase
if keyword_map[lower_word] and word ~= keyword_map[lower_word] then
local upper_word = keyword_map[lower_word]
vim.api.nvim_buf_set_text(bufnr, row - 1, word_start - 1, row - 1, word_end, { upper_word })
end
end
-- Auto-add semicolon after END_* keywords
local function auto_add_semicolon()
-- Only in SCL files
local ft = vim.bo.filetype
if ft ~= "scl" and ft ~= "udt" and ft ~= "db" then
return
end
local bufnr = vim.api.nvim_get_current_buf()
local row = vim.api.nvim_win_get_cursor(0)[1]
local line = vim.api.nvim_buf_get_lines(bufnr, row - 1, row, false)[1]
local trimmed = line:match("^%s*(.-)%s*$")
-- Check if line ends with an END_* keyword (without semicolon)
for _, end_kw in ipairs(end_keywords) do
if trimmed:upper() == end_kw then
-- Check if already has semicolon
if not line:match(";%s*$") then
local new_line = line .. ";"
vim.api.nvim_buf_set_lines(bufnr, row - 1, row, false, { new_line })
-- Move cursor to end of line
vim.api.nvim_win_set_cursor(0, { row, #new_line })
end
break
end
end
end
-- Setup auto-prefixing on specific triggers -- Setup auto-prefixing on specific triggers
function M.setup() function M.setup()
local augroup = vim.api.nvim_create_augroup("SCLAutoPrefix", { clear = true }) local augroup = vim.api.nvim_create_augroup("SCLAutoPrefix", { clear = true })
@@ -414,9 +497,15 @@ function M.setup()
vim.api.nvim_create_autocmd("InsertLeave", { vim.api.nvim_create_autocmd("InsertLeave", {
group = augroup, group = augroup,
callback = function() callback = function()
if vim.bo.filetype == "scl" then local ft = vim.bo.filetype
if ft == "scl" or ft == "udt" or ft == "db" then
-- Auto-add semicolon after END_* keywords
auto_add_semicolon()
-- Variable prefixing (only for scl)
if ft == "scl" then
auto_prefix_current_line() auto_prefix_current_line()
end end
end
end, end,
}) })
@@ -424,14 +513,20 @@ function M.setup()
vim.api.nvim_create_autocmd("TextChangedI", { vim.api.nvim_create_autocmd("TextChangedI", {
group = augroup, group = augroup,
callback = function() callback = function()
if vim.bo.filetype == "scl" then local ft = vim.bo.filetype
if ft == "scl" or ft == "udt" or ft == "db" then
local line = vim.api.nvim_get_current_line() local line = vim.api.nvim_get_current_line()
local cursor_col = vim.api.nvim_win_get_cursor(0)[2] local cursor_col = vim.api.nvim_win_get_cursor(0)[2]
if cursor_col > 0 and line:sub(cursor_col, cursor_col) == " " then if cursor_col > 0 and line:sub(cursor_col, cursor_col) == " " then
-- Auto-uppercase keywords
auto_uppercase_keyword()
-- Variable prefixing (only for scl)
if ft == "scl" then
auto_prefix_current_line() auto_prefix_current_line()
end end
end end
end
end, end,
}) })
@@ -484,6 +579,38 @@ function M.setup()
end end
end, end,
}) })
-- Auto-add semicolon when pressing Enter after END_* keyword
vim.api.nvim_create_autocmd("TextChangedI", {
group = augroup,
callback = function()
local ft = vim.bo.filetype
if ft ~= "scl" and ft ~= "udt" and ft ~= "db" then
return
end
local bufnr = vim.api.nvim_get_current_buf()
local row = vim.api.nvim_win_get_cursor(0)[1]
local line = vim.api.nvim_buf_get_lines(bufnr, row - 1, row, false)[1]
-- Check if we're on a new/empty line (Enter was pressed)
if line and line:match("^%s*$") and row > 1 then
local prev_line = vim.api.nvim_buf_get_lines(bufnr, row - 2, row - 1, false)[1]
if prev_line then
local trimmed = prev_line:match("^%s*(.-)%s*$")
-- Check if previous line is an END_* keyword without semicolon
for _, end_kw in ipairs(end_keywords) do
if trimmed:upper() == end_kw and not prev_line:match(";%s*$") then
-- Add semicolon to previous line
local new_prev = prev_line .. ";"
vim.api.nvim_buf_set_lines(bufnr, row - 2, row - 1, false, { new_prev })
break
end
end
end
end
end,
})
end end
-- Manual trigger function -- Manual trigger function