Last active
September 23, 2023 13:47
-
-
Save SmileYzn/62c102c69cb71c7b50dbfb4fecaefd28 to your computer and use it in GitHub Desktop.
A complete Lite-XL plugin for AMX Mod X build, syntax, auto-completetion and more
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- mod-version:3 | |
-- | |
-- Core requirements | |
local core = require("core") | |
local common = require("core.common") | |
local config = require("core.config") | |
local command = require("core.command") | |
local doc = require("core.doc") | |
local docView = require("core.docview") | |
local translate = require("core.doc.translate") | |
local keymap = require("core.keymap") | |
local rootView = require "core.rootview" | |
local style = require("core.style") | |
local syntax = require("core.syntax") | |
local tokenizer = require("core.tokenizer") | |
local view = require("core.view") | |
-- | |
-- Plugins requirements | |
local console = require("plugins.console") | |
local settings = require("plugins.settings") | |
-- | |
-- Settings of plugin | |
-- | |
-- Plugin Settings | |
config.plugins.amxx = common.merge | |
({ | |
-- | |
-- Compiler Path | |
compiler_path = "", | |
-- | |
-- Compiler include path | |
include = "include", | |
-- | |
-- Compiler executable name | |
compiler = "amxxpc.exe", | |
-- | |
-- Plugin output extension | |
extension = "amxx", | |
-- | |
-- Plugin extra output folder | |
output = "", | |
-- | |
-- Save current document on compile command | |
save = true, | |
-- | |
-- Clear console output before each execution | |
clear = true, | |
-- | |
-- The config specification used by the settings gui | |
config_spec = | |
{ | |
-- | |
-- Setting Label | |
name = "AMX Mod X Compiler", | |
-- | |
-- Compiler Path | |
{ | |
label = "AMX Mod X compiler folder", | |
description = "Folder of AMX Mod X compiler", | |
path = "compiler_path", | |
type = "directory", | |
default = "" | |
}, | |
-- | |
-- Compiler include path name | |
{ | |
label = "AMX Mod X name of 'include' folder", | |
description = "Name of 'include' folder", | |
path = "include", | |
type = "string", | |
default = "include" | |
}, | |
-- | |
-- Compiler executable name | |
{ | |
label = "AMX Mod X compiler executable", | |
description = "Name of compiler executable", | |
path = "compiler", | |
type = "string", | |
default = "amxxpc.exe" | |
}, | |
-- | |
-- Plugin output extension | |
{ | |
label = "AMX Mod X output extension", | |
description = "Extension of output file (plugin)", | |
path = "extension", | |
type = "string", | |
default = "amxx" | |
}, | |
-- | |
-- Plugin extra output folder | |
{ | |
label = "Copy plugin to specific path", | |
description = "Path to copy result plugin", | |
path = "output", | |
type = "directory", | |
exists = true, | |
default = "" | |
}, | |
-- | |
-- Save current document on compile command | |
{ | |
label = "Save current document on compile", | |
description = "Save current document before compile command", | |
path = "save", | |
type = "boolean", | |
default = true | |
}, | |
-- | |
-- Clear console output before each execution | |
{ | |
label = "Clear output before each execution", | |
description = "Clear output before each execution", | |
path = "clear", | |
type = "boolean", | |
default = true | |
}, | |
} | |
}, config.plugins.amxx) | |
-- | |
-- Language syntax table | |
local language_syntax = | |
{ | |
-- | |
-- Name | |
name = "amxx", | |
-- | |
-- File extensions | |
files = { "%.sma$", "%.inc$" }, | |
-- | |
-- Comment line | |
comment = "//", | |
-- | |
-- Comment block | |
block_comment = {"/*", "*/"}, | |
-- | |
-- Handle spaces ? | |
space_handling = true, | |
-- | |
-- Set language patterns | |
patterns = | |
{ | |
-- | |
-- Comment Line | |
{pattern = "//.*", type = "comment"}, | |
-- | |
-- Comment block | |
{pattern = {"/%*", "%*/"}, type = "comment"}, | |
-- | |
-- String double quotes | |
{pattern = {'"', '"', "^"}, type = "string"}, | |
-- | |
-- String single quotes | |
{pattern = {"'", "'", "^"}, type = "string"}, | |
-- | |
-- Hexadecimal | |
{pattern = "0x%x+", type = "number"}, | |
-- | |
-- Float | |
{pattern = "%d+[%d%.eE]*f?", type = "number"}, | |
-- | |
-- Numbers | |
{pattern = "%.?%d+f?", type = "number"}, | |
-- | |
-- Operators | |
{pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator"}, | |
-- | |
-- Method type declarations | |
{pattern = "native%s()[%a_][%w_]*", type = {"keyword", "function"}}, | |
{pattern = "forward%s()[%a_][%w_]*", type = {"keyword", "function"}}, | |
{pattern = "public%s()[%a_][%w_]*", type = {"keyword", "function"}}, | |
{pattern = "stock%s()[%a_][%w_]*", type = {"keyword", "function"}}, | |
-- | |
-- Static declarations | |
{pattern = "static()%s+()inline", type = { "keyword", "normal", "keyword"}}, | |
{pattern = "static()%s+()const", type = { "keyword", "normal", "keyword"}}, | |
{pattern = "static()%s+()[%a_][%w_]*", type = { "keyword", "normal", "literal"}}, | |
-- | |
-- Match function type declarations | |
{pattern = "[%a_][%w_]*()%*+()%s+()[%a_][%w_]*%f[%(]", type = { "literal", "operator", "normal", "function"}}, | |
{pattern = "[%a_][%w_]*()%s+()%*+()[%a_][%w_]*%f[%(]", type = { "literal", "normal", "operator", "function"}}, | |
{pattern = "[%a_][%w_]*()%s+()[%a_][%w_]*%f[%(]", type = { "literal", "normal", "function"}}, | |
-- | |
-- Match variable type declarations | |
{pattern = "[%a_][%w_]*()%*+()%s+()[%a_][%w_]*", type = { "literal", "operator", "normal", "normal"}}, | |
{pattern = "[%a_][%w_]*()%s+()%*+()[%a_][%w_]*", type = { "literal", "normal", "operator", "normal"}}, | |
{pattern = "[%a_][%w_]*()%s+()[%a_][%w_]*()%s*()[;,%[%)]", type = { "literal", "normal", "normal", "normal", "normal"}}, | |
{pattern = "[%a_][%w_]*()%s+()[%a_][%w_]*()%s*()=", type = { "literal", "normal", "normal", "normal", "operator"}}, | |
{pattern = "[%a_][%w_]*()&()%s+()[%a_][%w_]*", type = { "literal", "operator", "normal", "normal"}}, | |
{pattern = "[%a_][%w_]*()%s+()&()[%a_][%w_]*", type = { "literal", "normal", "operator", "normal"}}, | |
-- | |
-- Match scope operator element access | |
{pattern = "[%a_][%w_]*()%s*():", type = {"literal", "normal", "operator"}}, | |
-- | |
-- Uppercase constants of at least 2 chars in len | |
{pattern = "_?%u[%u_][%u%d_]*%f[%s%+%*%-%.%)%]}%?%^%%=/<>~|&;:,!]", type = "number"}, | |
-- | |
-- Magic constants | |
{pattern = "__[%u%l]+__", type = "number"}, | |
-- | |
-- All other functions | |
{pattern = "[%a_][%w_]*%f[(]", type = "function"}, | |
-- | |
-- Macros | |
{pattern = "^%s*#%s*define%s+()[%a_][%a%d_]*", type = { "keyword", "symbol"}}, | |
{pattern = "#%s*include%s+()<.->", type = { "keyword", "string"}}, | |
{pattern = "#%s*tryinclude%s+()<.->", type = { "keyword", "string"}}, | |
{pattern = "%f[#]#%s*[%a_][%w_]*", type = "keyword"}, | |
-- | |
-- Everything else to make the tokenizer work properly | |
{pattern = "[%a_][%w_]*", type = "symbol"}, | |
}, | |
-- | |
-- Load initial symbol list | |
symbols = | |
{ | |
-- | |
-- Statements | |
["assert"] = "keyword", | |
["break"] = "keyword", | |
["case"] = "keyword", | |
["continue"] = "keyword", | |
["default"] = "keyword", | |
["do"] = "keyword", | |
["else"] = "keyword", | |
["exit"] = "keyword", | |
["for"] = "keyword", | |
["goto"] = "keyword", | |
["if"] = "keyword", | |
["return"] = "keyword", | |
["sleep"] = "keyword", | |
["sstate"] = "keyword", | |
["switch"] = "keyword", | |
["while"] = "keyword", | |
-- | |
-- Operators | |
["defined"] = "keyword", | |
["sizeof"] = "keyword", | |
["state"] = "keyword", | |
["tagof"] = "keyword", | |
-- | |
-- Directives | |
["#assert"] = "keyword", | |
["#define"] = "keyword", | |
["#else"] = "keyword", | |
["#elseif"] = "keyword", | |
["#endif"] = "keyword", | |
["#endinput"] = "keyword", | |
["#error"] = "keyword", | |
["#file"] = "keyword", | |
["#if"] = "keyword", | |
["#include"] = "keyword", | |
["#line"] = "keyword", | |
["#pragma"] = "keyword", | |
["#section"] = "keyword", | |
["#tryinclude"] = "keyword", | |
["#undef"] = "keyword", | |
["#warning"] = "keyword", | |
-- | |
-- Other | |
["const"] = "keyword", | |
["forward"] = "keyword", | |
["native"] = "keyword", | |
["new"] = "keyword", | |
["operator"] = "keyword", | |
["public"] = "keyword", | |
["static"] = "keyword", | |
["stock"] = "keyword", | |
-- | |
-- Predefined constants | |
["cellbits"] = "keyword", | |
["cellmax"] = "keyword", | |
["cellmin"] = "keyword", | |
["charbits"] = "keyword", | |
["charmax"] = "keyword", | |
["charmin"] = "keyword", | |
["debug"] = "keyword", | |
["__line"] = "keyword", | |
["__Pawn"] = "keyword", | |
["ucharmax"] = "keyword", | |
-- | |
-- Literal | |
["true"] = "literal", | |
["false"] = "literal", | |
-- | |
-- Tag Names | |
["any"] = "keyword2", | |
["bool"] = "keyword2", | |
["Fixed"] = "keyword2", | |
["Float"] = "keyword2", | |
["Rational"] = "keyword2", | |
} | |
} | |
-- | |
-- Get file list from include folder | |
local function get_include_file_list() | |
-- Return file list | |
local file_list = {} | |
-- | |
-- Build themes path from current directory | |
local include_path = system.absolute_path(config.plugins.amxx.compiler_path .. PATHSEP .. config.plugins.amxx.include) | |
-- | |
-- Loop file names | |
for _, filename in ipairs(system.list_dir(include_path) or {}) do | |
-- | |
-- Full path of file | |
local file_path = system.absolute_path(include_path .. PATHSEP .. filename) | |
-- | |
-- Get file information | |
local file_info = system.get_file_info(file_path) | |
-- | |
-- If has file informationm type is file and have .yaml extension | |
if file_info and file_info.type == "file" and filename:match("%.inc$") then | |
-- | |
-- Remove extension from file name | |
local include_name = filename:gsub("%.inc$", "") | |
-- | |
-- If has include name | |
if include_name then -- DEBUG with amxmodx include only | |
file_list[include_name] = file_path | |
end | |
end | |
end | |
-- | |
-- Return file list | |
return file_list | |
end | |
-- | |
-- Read File Content | |
local function get_include_content(path) | |
-- | |
-- Content table | |
local content = {} | |
-- | |
-- Open file | |
local fp = io.open(path, "rb") | |
-- | |
-- If is not null | |
if fp then | |
-- | |
-- Current line | |
local line_number = 1 | |
-- | |
-- Read file content | |
for line in fp:lines() do | |
if line ~= nil and line ~= "" then | |
content[line_number] = line | |
end | |
-- | |
-- Increment line | |
line_number = line_number + 1 | |
end | |
-- | |
-- Close file pointer | |
fp:close() | |
-- | |
-- Return content | |
return content | |
end | |
-- | |
-- Return content | |
return content | |
end | |
-- | |
-- Parse functions: native, forward, public and stock | |
local function parse_function(path, number, line) | |
-- | |
-- Match function with tag | |
local type, tag, body = line:match("^(%w+)%s(%w+):(.+)") | |
-- | |
-- If has type, tag and body | |
if type and tag and body then | |
-- | |
-- Get name | |
local name = line:match("[%a_][%w_]*%f[(]") | |
-- | |
-- If is not empty | |
if name then | |
-- | |
-- Function data | |
local function_data = {} | |
-- | |
-- Store function path | |
function_data.path = path | |
-- | |
-- Store line number | |
function_data.line = number | |
-- | |
-- Store function name | |
function_data.name = name | |
-- | |
-- Type (native, forward, public, stock) | |
function_data.type = type | |
-- | |
-- Store return type (bool, Float, any) | |
function_data.tag = tag | |
-- | |
-- Auto Complete body (The text that will used to insert on documents) | |
function_data.body = body | |
-- | |
-- Set complete function line | |
function_data.text = line | |
-- | |
-- Function documentation | |
function_data.doc = line | |
-- | |
-- Return data | |
return function_data | |
end | |
end | |
-- | |
-- Match with functions without tag | |
type, body = line:match("^(%w+)%s(.+)") | |
-- | |
-- If has type and body | |
if type and body then | |
-- | |
-- Get Name | |
local name = line:match("[%a_][%w_]*%f[(]") | |
-- | |
-- If is not empty | |
if name then | |
-- | |
-- Function data | |
local function_data = {} | |
-- | |
-- Store function path | |
function_data.path = path | |
-- | |
-- Store line number | |
function_data.line = number | |
-- | |
-- Store function name | |
function_data.name = name | |
-- | |
-- Type (native, forward, public, stock) | |
function_data.type = type | |
-- | |
-- Store return type (bool, Float, any) | |
function_data.tag = "" | |
-- | |
-- Auto Complete body (The text that will used to insert on documents) | |
function_data.body = body | |
-- | |
-- Set complete function line | |
function_data.text = line | |
-- | |
-- Function documentation | |
function_data.doc = line | |
-- | |
-- Return data | |
return function_data | |
end | |
end | |
-- | |
-- Return function data | |
return nil | |
end | |
-- | |
-- Parse function documentation of function starting from function line number | |
local function parse_function_doc(number, content_table) | |
-- | |
-- Documentation content, set it as default of current line | |
local doc_content = "" | |
-- | |
-- Check previous line is end of comment or something else | |
if content_table[number-1] ~= nil and (content_table[number-1]:match(language_syntax.patterns[2].pattern[2]) or content_table[number-1]:match(language_syntax.patterns[1].pattern)) then | |
-- | |
-- Loop reverse from function line number | |
for i = number, 1, -1 do | |
-- | |
-- Check if is not nil | |
if content_table[i] ~= nil then | |
-- | |
-- Reversed concatenation, we are doing reverse loop thing | |
doc_content = content_table[i] .. '\n' .. doc_content | |
-- | |
-- If documentation is start of comment | |
if content_table[i]:match(language_syntax.patterns[2].pattern[1]) or content_table[i]:match(language_syntax.patterns[1].pattern) then | |
-- | |
-- We have done here, exit loop | |
break | |
end | |
end | |
end | |
end | |
-- | |
-- Return loaded contetn | |
return doc_content | |
end | |
-- | |
-- Parse include file | |
local function parse_include_file(path) | |
-- | |
-- Function List | |
local function_list = {} | |
-- | |
-- Get include conetnt on table | |
local content_table = get_include_content(path) | |
-- | |
-- If content table is not empty | |
if content_table then | |
-- | |
-- Loop lines | |
for number, line in pairs(content_table) do | |
-- | |
-- Load function data for this line | |
local function_data = parse_function(path, number, line) | |
-- | |
-- If is not empty | |
if function_data then | |
-- | |
-- Get function documentation | |
local documentation = parse_function_doc(number, content_table) | |
-- | |
-- If file info is not nil | |
if documentation ~= nil and documentation ~= "" then | |
-- | |
-- Set documentation on function data | |
function_data.doc = string.format("%s (%d) - %s\n%s\n\n%s", common.basename(path), number, function_data.type, string.rep("-", 40), documentation) | |
else | |
-- | |
-- Set documentation on function data from default | |
function_data.doc = string.format("%s (%d) - %s\n%s\n\n%s", common.basename(path), number, function_data.type, string.rep("-", 40), function_data.doc) | |
end | |
-- | |
-- If has tag in function, pass this as symbol to syntax | |
if function_data.tag and function_data.tag ~= "" then | |
-- | |
-- If not exists in table yet | |
if function_list[function_data.tag] == nil then | |
-- | |
-- Insert tag of function in list | |
function_list[function_data.tag] = function_data.tag | |
end | |
end | |
-- | |
-- Insert function in list | |
function_list[function_data.name] = function_data | |
end | |
end | |
end | |
-- | |
-- Return function list | |
return function_list | |
end | |
-- | |
-- On Hover | |
local function on_hover(suggestions_idx, item) | |
-- | |
-- Some event here, only unused | |
end | |
-- | |
-- On Select | |
local function on_select(suggestions_idx, item) | |
-- | |
-- Current document view | |
local doc = core.active_view.doc | |
-- | |
-- Get current selection | |
local line2, col2 = doc:get_selection() | |
-- | |
-- Get current cursor position | |
local line1, col1 = doc:position_offset(line2, col2, translate.start_of_word) | |
-- | |
-- Get current partial typed text | |
local current_partial = doc:get_text(line1, col1, line2, col2) | |
-- | |
-- Get size of partial | |
local sz = #current_partial | |
-- | |
-- Loop all selections in document view | |
for idx, line1, col1, line2, col2 in doc:get_selections(true) do | |
-- | |
-- Previous column idx | |
local n = col1 - 1 | |
-- | |
-- Get current line of document view | |
local line = doc.lines[line1] | |
-- | |
-- For each column | |
for i = 1, sz + 1 do | |
-- | |
-- Remove one character space | |
local j = sz - i | |
-- | |
-- Remove from line the character | |
local subline = line:sub(n - j, n) | |
-- | |
-- Remove from partial line the characters | |
local subpartial = current_partial:sub(i, -1) | |
-- | |
-- If partial match is equal match of subline | |
if subpartial == subline then | |
-- | |
-- Remove partial typed match | |
doc:remove(line1, col1, line2, n - j) | |
-- | |
-- Break | |
break | |
end | |
end | |
end | |
-- | |
-- Input autocomplete text | |
doc:text_input(item.data) | |
-- | |
-- Return to autocomplete plugin that text is placed | |
return true | |
end | |
-- | |
-- Update auto complete | |
local function update_auto_complete() | |
-- | |
-- List of include files | |
local include_list = get_include_file_list() | |
-- | |
-- Loop include list | |
for include_name, path in pairs(include_list) do | |
-- | |
-- Get function list and tags from that include file | |
local function_list = parse_include_file(path) | |
-- | |
-- Loop function list result | |
for name, item_data in pairs(function_list) do | |
-- | |
-- If type is table, set function in language syntax | |
if type(item_data) == "table" then | |
-- | |
-- Include on language syntax | |
language_syntax.symbols[name] = | |
{ | |
-- | |
-- Name of function to show in list | |
text = name, | |
-- | |
-- Type of autocomplete item | |
info = (style.syntax[item_data.type] ~= nil and item_data.type or "function"), | |
-- | |
-- Icon to show in list: "comment", "function", "keyword", "operator" and others | |
icon = "function", | |
-- | |
-- Description of that function to show | |
desc = item_data.doc, | |
-- | |
-- On hover an item on auto complete list | |
onhover = on_hover, | |
-- | |
-- On select an item to replace in | |
onselect = on_select, | |
-- | |
-- Parameter of onselet or onhover events (Send text of item to autocomplete) | |
data = item_data.body, | |
-- | |
-- Include file | |
path = item_data.path, | |
-- | |
-- Include file line | |
line = item_data.line | |
} | |
-- | |
-- If type is string, parse to symbol table as symbol | |
else | |
-- | |
-- If if the symbol is not set yet | |
if language_syntax.symbols[name] == nil then | |
-- | |
-- Include on language syntax | |
language_syntax.symbols[name] = | |
{ | |
-- | |
-- Name of function to show in list | |
text = name, | |
-- | |
-- Type of autocomplete item | |
info = "keyword2", | |
-- | |
-- Icon to show in list: "comment", "function", "keyword", "operator" and others | |
icon = "keyword", | |
-- | |
-- Description of that function to show | |
desc = string.format("%s Tag Name - %s\n%s\n\n%s", common.basename(path), name, string.rep("-", 40), name), | |
} | |
end | |
end | |
end | |
end | |
end | |
-- | |
-- Fix tokenizer of docview by Guldoman | |
local old_each_token = tokenizer.each_token | |
-- | |
-- Fix tokenizer of docview by Guldoman | |
function tokenizer.each_token(...) | |
-- | |
-- Iterate over tokens | |
local iter, t, _ = old_each_token(...) | |
-- | |
-- Return anonymous function result for each iterator | |
return function(t, i) | |
-- | |
-- Get token data from iterator | |
local i, _type, text = iter(t, i) | |
-- | |
-- If type is table | |
if type(_type) == "table" then | |
-- | |
-- Set info parameter | |
_type = _type.info | |
end | |
-- | |
-- Return parameters for that token | |
return i, _type, text | |
end, t, -1 | |
end | |
-- | |
-- On release mouse click | |
local on_mouse_released = docView.on_mouse_released | |
-- | |
-- On release mouse click | |
function docView:on_mouse_released(button, x, y, ...) | |
-- | |
-- Call original release mouse click function | |
on_mouse_released(self, button, x, y, ...) | |
-- | |
-- If button was left and ALT was pressed | |
if button == "left" and keymap.modkeys["alt"] then | |
-- | |
-- Get line and column of mouse position of click | |
local line, col = self:resolve_screen_position(x, y) | |
-- | |
-- If is not null | |
if line and col then | |
-- | |
-- For each token of lile | |
for _, type, text in self.doc.highlighter:each_token(line) do | |
-- | |
-- Check if has text, and the type of token | |
if text and type == "function" or type == "native" or type == "forward" or type == "public" or type == "stock" then | |
-- | |
-- Trim spaces of text | |
text = text:match("^%s*(.-)%s*$") | |
-- | |
-- If is not null | |
if text then | |
-- | |
-- Check if has syntax symbols with that text, and have path and line of file | |
if language_syntax.symbols[text] and language_syntax.symbols[text].path and language_syntax.symbols[text].line then | |
-- | |
-- Try open doc | |
core.try(function() | |
-- | |
-- Try to open doc on root view from specified path | |
local dv = core.root_view:open_doc(core.open_doc(language_syntax.symbols[text].path)) | |
-- | |
-- Update root view layout | |
core.root_view.root_node:update_layout() | |
-- | |
-- Set selection on line of syntax symbol in target file | |
dv.doc:set_selection(language_syntax.symbols[text].line, 1) | |
-- | |
-- Scroll to target line | |
dv:scroll_to_line(language_syntax.symbols[text].line, false, true) | |
end) | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
-- | |
-- On view mouse pressed | |
local amxx_on_mouse_pressed = view.on_mouse_pressed | |
-- | |
-- On view mouse pressed callback | |
function view:on_mouse_pressed(button, x, y, clicks) | |
-- | |
-- Store result of original function | |
local result = amxx_on_mouse_pressed(self, button, x, y, clicks) | |
-- | |
-- If current mouse click is not in console | |
if self:get_name() ~= "Console" and console.isVisible() then | |
-- | |
-- Close console | |
console.close() | |
end | |
-- | |
-- Return result | |
return result | |
end | |
-- | |
-- Ask for compiler path | |
local amxx_ask_compiler_path = function(execute_build) | |
-- | |
-- AMX Mod X compiler executable | |
local executablePath = system.absolute_path(config.plugins.amxx.compiler_path .. PATHSEP .. config.plugins.amxx.compiler) | |
--- | |
-- Check executable before try to compile | |
if system.get_file_info(executablePath) == nil then | |
-- | |
-- Show error message | |
core.error("AMX Mod X Compiler: Compiler not found: " .. executablePath) | |
-- | |
-- Call for ask for compiler path | |
core.nag_view:show("AMX Mod X compiler not found", "AMX Mod X compiler not found, did you want to choose path?", | |
{ | |
{text = "Yes", default_yes = true}, | |
{text = "No", default_no = false} | |
}, function(item) | |
-- | |
-- If yes | |
if item.text == "Yes" then | |
-- | |
-- Create thread, the nagview is designed that | |
core.add_thread(function() | |
-- | |
-- Try to get that path and set | |
core.command_view:enter("Specify the AMX Mod X Compiler path", | |
{ | |
-- | |
-- Valudate input text to check if path exists | |
validate = function(text) | |
-- | |
-- Check input text if is a valid directory | |
if system.get_file_info(text) == nil then | |
-- | |
-- Message | |
core.error(string.format("Specified path '%s' do not exists.", text)) | |
-- | |
-- Path is not valid | |
return false | |
end | |
-- | |
-- Path is valid | |
return true | |
end, | |
-- | |
-- On submit | |
submit = function(text) | |
-- | |
-- Get path | |
local path = system.absolute_path(text) | |
-- | |
-- If path is not found, log error | |
if system.get_file_info(path) == nil then | |
-- | |
-- Error message | |
core.error(string.format("Specified path not found: %s.",path)) | |
else | |
-- | |
-- Set path on current plugin | |
config.plugins.amxx.compiler_path = path | |
-- | |
-- If has settings of plugins | |
if settings.config["plugins"] then | |
-- | |
-- Create for ths plugin | |
settings.config["plugins"]["amxx"] = {compiler_path = path} | |
end | |
-- | |
-- Reload settings plugin to get new config loaded and saved | |
settings.ui:new() | |
-- | |
-- Success message | |
core.error("AMX Mod X Compiler set: " .. path) | |
-- | |
-- if has execute build | |
if execute_build then | |
-- | |
-- Try to run | |
command.perform("amxx:build") | |
end | |
end | |
end, | |
-- | |
-- Suggest directory for amxx compiler finder | |
suggest = function(text) | |
-- Expand home directory from text | |
text = common.home_expand(text) | |
-- | |
-- Get base dir from project directory | |
local basedir = common.dirname(core.project_dir) | |
-- | |
-- Return encoded list with directory fround and recent suggestions | |
return common.home_encode_list((basedir and text == basedir .. PATHSEP or text == "") and core.recent_projects or common.dir_path_suggest(text)) | |
end | |
}) | |
end) | |
end | |
end) | |
-- | |
-- Return false | |
return false | |
end | |
-- | |
-- Return true | |
return true | |
end | |
-- | |
-- Check if current file is valid | |
local amxx_check_active_file_name = function() | |
-- | |
-- If has active view file name | |
if core.active_view.doc and core.active_view.doc.filename then | |
-- | |
-- Loop files sytax table | |
for _, pattern in pairs(language_syntax.files) do | |
-- | |
-- If pattern match with filename | |
if string.match(core.active_view.doc.filename, pattern) then | |
-- | |
-- Return true | |
return true | |
end | |
end | |
end | |
-- | |
-- Return false | |
return false | |
end | |
-- | |
-- Build plugin function | |
local amxx_build_plugin_func = function() | |
-- | |
-- If has an valid extension | |
if amxx_check_active_file_name() then | |
-- | |
-- AMX Mod X compiler executable | |
local executablePath = system.absolute_path(config.plugins.amxx.compiler_path .. PATHSEP .. config.plugins.amxx.compiler) | |
--- | |
-- Check executable before try to compile | |
if amxx_ask_compiler_path(true) then | |
-- | |
-- AMX Mod X binary default output file path (Convert old .sma extension to .amxx) | |
local outputPath = string.format("%s.%s", core.active_view.doc.abs_filename:match("(.+)%..+"), config.plugins.amxx.extension) | |
-- | |
-- If output path setting is not empty | |
if config.plugins.amxx.output and config.plugins.amxx.output ~= "" then | |
-- | |
-- Construct output path based on path specified | |
outputPath = config.plugins.amxx.output .. PATHSEP .. string.format("%s.%s", common.basename(core.active_view.doc.filename):match("(.+)%..+"), config.plugins.amxx.extension) | |
end | |
-- | |
-- AMX Mod X compiler include | |
local includePath = config.plugins.amxx.path .. PATHSEP .. config.plugins.amxx.include | |
-- | |
-- AMX Mod X compiler command line | |
local compileCommand = string.format("%s -i%s -o%s %s", executablePath, includePath, outputPath, core.active_view.doc.abs_filename) | |
-- | |
-- If compilercommand is null | |
if compileCommand == nil then | |
-- | |
-- Send error message | |
core.error("AMX Mod X Compiler: Command is empty.") | |
else | |
-- | |
-- Save current file each run | |
if config.plugins.amxx.save == true then | |
-- | |
-- Save | |
core.active_view.doc:save() | |
end | |
-- | |
-- Clear console on each run | |
if config.plugins.amxx.clear == true then | |
-- | |
-- Clear console | |
console.clear(); | |
end | |
-- | |
-- Run console command | |
console.run | |
{ | |
-- | |
-- Command | |
command = compileCommand, | |
-- | |
-- Output warning / errors pattern | |
file_pattern = "(.*)%((.*)%) : (.*)", | |
-- | |
-- File prefix separator | |
file_prefix = ".", | |
-- | |
-- Current directory of command | |
cwd = ".", | |
-- | |
-- Error pattern | |
error_pattern = "error", | |
-- | |
-- Warning pattern | |
warning_pattern = "warning", | |
-- | |
-- On command end | |
on_complete = function() | |
core.log("AMX Mod X Compiler: Compilation Complete") | |
end | |
} | |
end | |
end | |
else | |
-- | |
-- Error message if is not an valid file | |
core.error("AMX Mod X Compiler: " .. common.basename(core.active_view.doc.filename) .. " is not a AMX Mod X source file") | |
end | |
end | |
-- | |
-- On open document | |
local RootView_open_doc = rootView.open_doc | |
-- | |
-- On open document | |
function rootView:open_doc(doc) | |
-- | |
-- Execute original function to docview | |
local docview = RootView_open_doc(self, doc) | |
-- | |
-- If is an .sma or .inc file | |
if amxx_check_active_file_name() then | |
-- | |
-- Ask for compiler path if not exists | |
amxx_ask_compiler_path(false) | |
end | |
-- | |
-- Return result | |
return docview | |
end | |
-- | |
-- Update auto complete list | |
core.add_thread(function() | |
update_auto_complete() | |
end) | |
-- | |
-- Add or reload language syntax | |
syntax.add(language_syntax) | |
-- | |
-- Add commands on document view | |
command.add("core.docview", | |
{ | |
-- | |
-- AMXX build command | |
["amxx:build"] = amxx_build_plugin_func | |
}) | |
-- | |
-- Add CTRL + B | |
keymap.add({["ctrl+b"] = "amxx:build"}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment