diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 9221d17..301b64e --- a/README.md +++ b/README.md @@ -1,164 +1,2 @@ -![Screenshot](https://github.com/uriid1/pimp-lua/blob/main/screenshots/pimp_logo.png) - -Russian | [English](README_EN.md)
-На текущий момент, наиболее полная документация доступна только на русском языке.
-Поддерживаются версии lua5.4 и luajit - -## Pimp -Модуль предназначен для красивой печати всех lua-типов, в особенности таблиц.
-А так же служит для простой отладки с использованием встроенной lua библиотеки debug.
-Основная цель модуля — заменить print более совершенным инструментом.
- -![Screenshot](https://github.com/uriid1/pimp-lua/blob/main/screenshots/screenshot.png) - -## Установка -```bash -luarocks install pimp -``` - -## Особенности lua и скорость работы модуля -1. В lua нет возможности получить простым путем имя какой-либо переменной из стейта. -Поэтому имена переменных, приходится буквально искать по значению или адресу. -В случае с таблицами, coroutine, функциями и userdata чаще всего нет проблем найти имя переменной, так как у этих типов есть адрес. С локальными переменными всё намного сложнее, получить имя по значению можно, но если будут upvalue или в локальном стеке переменные с таким же значением, нельзя гарантировать, что конкретное имя переменной будет соответствует конкретному значению. Поэтому по умолчанию в модуле отключен поиск имён локальных переменных, но вы можете включить этот режим, используя метод - `pimp:enableFindLocalName()` - -2. Модуль предназначен для тестирования и отладки кода, он значительно замедляет вашу программу, поэтому используйте его по назначению. А что бы не пришлось каждый раз удалять методы модуля из кода, существует возможность отключить работу pimp - `pimp:disable()`. И так же включить - `pimp:enable()`. Либо же отключить глобально - `pimp:globalDisable()`. - -## Рекомендация по загрузке модуля -```lua --- Где-нибудь в точке входа в проект -_G.p = require('pimp') - --- При использовании Windows, лучше отключить отображение цветов -p:disableColor() -``` - -## Инспектирование lua-типов -```lua -p('Pimp Module!') -p("^([a-z0-9]+)@([%w%d]+)$") -p(true, false, nil) -p(function() end) -p(io.stderr) -p(10000, math.pi) -p(0/0, -1/0, 1/0) - -local test = function () end -p(function() end, test) - -local co = coroutine.create(function() end) -p(co) - -if box then - p(box.NULL) -end -``` - -## Инспектирование таблиц -```lua --- Таблица будет распечатана --- переменной table_name присвоиться ссылка на таблицу -local table_name = p({ - name = "John", - age = 30, - city = "New York" -}) -``` - -## Инспектирование функций -```lua -local function sum(a, b) - p(a, b) - return a + b -end - --- В переменной result_sum будет число 15 -local result_sum = p(sum(10, 5)) -``` - -**Включение и отключение вывода* -```lua -p:disable() -p('Hello') -p:enable() - -p('World') -``` - -**Смена префикса** -```lua -p:setPrefix({ prefix = 'INFO', sep = '|-> ' }) -p('Wow! It\'s new prefix!') -p:resetPrefix() -``` -``` -INFO|-> file.lua:2: 'Wow! It's new preffix!': [length 22] -``` - -**Логирование** -```lua ---- Модуль log работает синхронно -p.log.trace('Trace message') -p.log.debug('Debug message') -p.log.info('Info message') -p.log.warn('Warn message') -p.log.error('Error message') -p.log.fatal('Fatal message') -``` - -Все методы для настройки
-```lua --- Возвращает префикс по умолчанию -p:resetPrefix() - --- Вкл/Выкл работы модуля --- при этом pimp возвращает все методы, которые в него попадают -p:disable() -p:enable() - --- Глобальное отключение вывода из pimp --- игнорируя методы p:enable() / p:disable() -p:globalDisable() - --- Установка паттерна на захват пути --- пример re_str = "foo%-bar/(.+)" -p:matchPath(re_str) - --- Вкл/Выкл отображение полных путей -p:disableFullPath() -p:enableFullPath() - --- Вкл/Выкл отображение цветов -p:disableColor() -p:enableColor() - --- Вкл/Выкл отображения local, global, method, upvalue -p:enableVisibility() -p:disableVisibility() - --- Вкл/Выкл отображения типов -p:enableType() -p:disableType() - --- Вкл/Выкл отображения адресов таблиц -p:enableTableAddr() -p:disableTableAddr() - --- Вкл/Выкл отображения всех функций, из которых был вызов pimp-а -p:enableFullCallStack() -p:disableFullCallStack() - --- Вкл/Выкл стека функций в виде лесенки -p:enableCallStackLadder() -p:disableCallStackLadder() - --- (Экспериментально) Вкл/Выкл поиска имени локальной переменной -p:enableFindLocalName() -p:disableFindLocalName() - --- Метод сдампит таблицу, без цветов --- и вернет текстом дамп -p.pp(t) - --- Метод вернет таблицей локальные переменные по заданному уровню -p.getLocalEnv(level) -``` +# TODO: Pimp v3.0.0 + - https://github.com/uriid1/pimp-lua/issues/7 diff --git a/README_EN.md b/README_EN.md deleted file mode 100755 index 0387083..0000000 --- a/README_EN.md +++ /dev/null @@ -1,108 +0,0 @@ -![Screenshot](https://github.com/uriid1/pimp-lua/blob/main/screenshots/pimp_logo.png) - -[Russian](README.md) | English
-Support lua5.4 and luajit - -## Overview -Module for pretty-printing tables and text, as well as for simple debugging using Lua's built-in debug methods. The main goal of the module is to replace print with a more advanced tool. - -![Screenshot](https://github.com/uriid1/pimp-lua/blob/main/screenshots/screenshot.png) - -## Installing -```bash -luarocks install pimp -``` - -## Inspect Variables -```lua -p('Pimp Module!') -p(true, false, nil) -p(function() end) -p(io.stderr) -p(10000, math.pi) -p(0/0, -1/0, 1/0) - -local test = function () end -p(function() end, test) - -local co = coroutine.create(function() end) -p(co) - -if box then - p(box.NULL) -end -``` - -## Inspect Tables -```lua -local table_name = p({ - name = "John", - age = 30, - city = "New York" -}) -``` - -## Inspect Functions -```lua -local function sum(a, b) - p(a, b) - return a + b -end - -local result_sum = p(sum(10, 5)) -``` - -**Disable or Enable output** -```lua -p:disable() -p('Hello') -p:enable() - -p('World') -``` - -**Change prefix** -```lua -p:setPrefix({ prefix = 'INFO', sep = '|-> ' }) -p('Wow! It\'s new prefix!') -p:resetPrefix() -``` -``` -INFO|-> file.lua:2: 'Wow! It's new preffix!': [length 22] -``` - -**logging** -```lua -p.log.trace('Trace message') -p.log.debug('Debug message') -p.log.info('Info message') -p.log.warn('Warn message') -p.log.error('Error message') -p.log.fatal('Fatal message') -``` - -Extensive configuration
-```lua -p:resetPrefix() -p:disable() -p:enable() -p:globalDisable() -p:matchPath(str) -p:disableFullPath() -p:enableFullPath() -p:disableColor() -p:enableColor() -p:enableVisibility() -p:disableVisibility() -p:enableType() -p:disableType() -p:enableTableAddr() -p:disableTableAddr() -p:enableFullCallStack() -p:disableFullCallStack() -p.pp(t) -p:enableFindLocalName() -p:disableFindLocalName() -p:enableCallStackLadder() -p:disableCallStackLadder() -``` diff --git a/ldoc/index.html b/ldoc/index.html deleted file mode 100644 index ca86526..0000000 --- a/ldoc/index.html +++ /dev/null @@ -1,800 +0,0 @@ - - - - - Reference - - - - -
- -
- -
-
-
- - -
- - - - - - -
- -

Module pimp

-

Module for pretty-printing tables and debugging utilities

-

- - -

Functions

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
debug (...)Debug function to print values with context information
msg (text)Print simple text message with timestamp
setPrefix (param)Set prefix and separator
resetPrefix ()Reset prefix and separator to defaults
disable ()Disable debug output
enable ()Enable debug output
globalDisable ()Globally disable pimp calls
matchPath (str)Set path matching pattern
disableFullPath ()Disable full path output
enableFullPath ()Enable full path output
disableColor ()Disable color output
enableColor ()Enable color output
enableEscapeNonAscii ()Enable escaping of non-ASCII characters
disableEscapeNonAscii ()Disable escaping of non-ASCII characters
enableVisibility ()Enable visibility information display
disableVisibility ()Disable visibility information display
enableType ()Enable type information display
disableType ()Disable type information display
enableTableAddr ()Enable table address display
disableTableAddr ()Disable table address display
enableFullCallStack ()Enable full call stack display
disableFullCallStack ()Disable full call stack display
pp (t)Return table with pretty-print without respecting output config
ppnc (t)Return table with pretty-print without color and output config
getLocalEnv (level)Get local environment variables
enableFindLocalName ()Enable finding local variable names
disableFindLocalName ()Disable finding local variable names
enableCallStackLadder ()Enable call stack ladder display
disableCallStackLadder ()Disable call stack ladder display
decimalToHexadecimal ()Enable decimal to hexadecimal conversion
- -
-
- - -

Functions

- -
-
- - debug (...) -
-
- Debug function to print values with context information - - -

Parameters:

-
    -
  • ... - Values to debug -
  • -
- -

Returns:

-
    - - The input values (for chaining) -
- - - - -
-
- - msg (text) -
-
- Print simple text message with timestamp - - -

Parameters:

-
    -
  • text - Text to print -
  • -
- - - - - -
-
- - setPrefix (param) -
-
- Set prefix and separator - - -

Parameters:

-
    -
  • param - Table with prefix and separator { prefix='cool', sep='->' } -
  • -
- -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - resetPrefix () -
-
- Reset prefix and separator to defaults - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disable () -
-
- Disable debug output - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - enable () -
-
- Enable debug output - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - globalDisable () -
-
- Globally disable pimp calls - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - matchPath (str) -
-
- Set path matching pattern - - -

Parameters:

-
    -
  • str - Lua pattern to match paths -
  • -
- -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disableFullPath () -
-
- Disable full path output - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - enableFullPath () -
-
- Enable full path output - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disableColor () -
-
- Disable color output - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - enableColor () -
-
- Enable color output - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - enableEscapeNonAscii () -
-
- Enable escaping of non-ASCII characters - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disableEscapeNonAscii () -
-
- Disable escaping of non-ASCII characters - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - enableVisibility () -
-
- Enable visibility information display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disableVisibility () -
-
- Disable visibility information display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - enableType () -
-
- Enable type information display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disableType () -
-
- Disable type information display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - enableTableAddr () -
-
- Enable table address display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disableTableAddr () -
-
- Disable table address display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - enableFullCallStack () -
-
- Enable full call stack display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disableFullCallStack () -
-
- Disable full call stack display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - pp (t) -
-
- Return table with pretty-print without respecting output config - - -

Parameters:

-
    -
  • t - Table to pretty-print -
  • -
- -

Returns:

-
    - - Pretty-printed string representation -
- - - - -
-
- - ppnc (t) -
-
- Return table with pretty-print without color and output config - - -

Parameters:

-
    -
  • t - Table to pretty-print -
  • -
- -

Returns:

-
    - - Pretty-printed string representation without color -
- - - - -
-
- - getLocalEnv (level) -
-
- Get local environment variables - - -

Parameters:

-
    -
  • level - Stack level to get locals from -
  • -
- -

Returns:

-
    - - Table with local variables -
- - - - -
-
- - enableFindLocalName () -
-
- Enable finding local variable names - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disableFindLocalName () -
-
- Disable finding local variable names - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - enableCallStackLadder () -
-
- Enable call stack ladder display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - disableCallStackLadder () -
-
- Disable call stack ladder display - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - decimalToHexadecimal () -
-
- Enable decimal to hexadecimal conversion - - - -

Returns:

-
    - - self for method chaining -
- - - - -
-
- - -
-
-
-generated by LDoc 1.5.0 -Last updated 2025-03-06 16:12:57 -
-
- - diff --git a/ldoc/ldoc_new.css b/ldoc/ldoc_new.css deleted file mode 100644 index 13fef16..0000000 --- a/ldoc/ldoc_new.css +++ /dev/null @@ -1,290 +0,0 @@ -body { - color: #47555c; - font-size: 16px; - font-family: "Open Sans", sans-serif; - margin: 0; - background: #eff4ff; -} - -a:link { color: #008fee; } -a:visited { color: #008fee; } -a:hover { color: #22a7ff; } - -h1 { font-size:26px; font-weight: normal; } -h2 { font-size:22px; font-weight: normal; } -h3 { font-size:18px; font-weight: normal; } -h4 { font-size:16px; font-weight: bold; } - -hr { - height: 1px; - background: #c1cce4; - border: 0px; - margin: 15px 0; -} - -code, tt { - font-family: monospace; -} -span.parameter { - font-family: monospace; - font-weight: bold; - color: rgb(99, 115, 131); -} -span.parameter:after { - content:":"; -} -span.types:before { - content:"("; -} -span.types:after { - content:")"; -} -.type { - font-weight: bold; font-style:italic -} - -p.name { - font-family: "Andale Mono", monospace; -} - -#navigation { - float: left; - background-color: white; - border-right: 1px solid #d3dbec; - border-bottom: 1px solid #d3dbec; - - width: 14em; - vertical-align: top; - overflow: visible; -} - -#navigation br { - display: none; -} - -#navigation h1 { - background-color: white; - border-bottom: 1px solid #d3dbec; - padding: 15px; - margin-top: 0px; - margin-bottom: 0px; -} - -#navigation h2 { - font-size: 18px; - background-color: white; - border-bottom: 1px solid #d3dbec; - padding-left: 15px; - padding-right: 15px; - padding-top: 10px; - padding-bottom: 10px; - margin-top: 30px; - margin-bottom: 0px; -} - -#content h1 { - background-color: #2c3e67; - color: white; - padding: 15px; - margin: 0px; -} - -#content h2 { - background-color: #6c7ea7; - color: white; - padding: 15px; - padding-top: 15px; - padding-bottom: 15px; - margin-top: 0px; -} - -#content h2 a { - background-color: #6c7ea7; - color: white; - text-decoration: none; -} - -#content h2 a:hover { - text-decoration: underline; -} - -#content h3 { - font-style: italic; - padding-top: 15px; - padding-bottom: 4px; - margin-right: 15px; - margin-left: 15px; - margin-bottom: 5px; - border-bottom: solid 1px #bcd; -} - -#content h4 { - margin-right: 15px; - margin-left: 15px; - border-bottom: solid 1px #bcd; -} - -#content pre { - margin: 15px; -} - -pre { - background-color: rgb(50, 55, 68); - color: white; - border-radius: 3px; - /* border: 1px solid #C0C0C0; /* silver */ - padding: 15px; - overflow: auto; - font-family: "Andale Mono", monospace; -} - -#content ul pre.example { - margin-left: 0px; -} - -table.index { -/* border: 1px #00007f; */ -} -table.index td { text-align: left; vertical-align: top; } - -#navigation ul -{ - font-size:1em; - list-style-type: none; - margin: 1px 1px 10px 1px; -} - -#navigation li { - text-indent: -1em; - display: block; - margin: 3px 0px 0px 22px; -} - -#navigation li li a { - margin: 0px 3px 0px -1em; -} - -#content { - margin-left: 14em; -} - -#content p { - padding-left: 15px; - padding-right: 15px; -} - -#content table { - padding-left: 15px; - padding-right: 15px; - background-color: white; -} - -#content p, #content table, #content ol, #content ul, #content dl { - max-width: 900px; -} - -#about { - padding: 15px; - padding-left: 16em; - background-color: white; - border-top: 1px solid #d3dbec; - border-bottom: 1px solid #d3dbec; -} - -table.module_list, table.function_list { - border-width: 1px; - border-style: solid; - border-color: #cccccc; - border-collapse: collapse; - margin: 15px; -} -table.module_list td, table.function_list td { - border-width: 1px; - padding-left: 10px; - padding-right: 10px; - padding-top: 5px; - padding-bottom: 5px; - border: solid 1px rgb(193, 204, 228); -} -table.module_list td.name, table.function_list td.name { - background-color: white; min-width: 200px; border-right-width: 0px; -} -table.module_list td.summary, table.function_list td.summary { - background-color: white; width: 100%; border-left-width: 0px; -} - -dl.function { - margin-right: 15px; - margin-left: 15px; - border-bottom: solid 1px rgb(193, 204, 228); - border-left: solid 1px rgb(193, 204, 228); - border-right: solid 1px rgb(193, 204, 228); - background-color: white; -} - -dl.function dt { - color: rgb(99, 123, 188); - font-family: monospace; - border-top: solid 1px rgb(193, 204, 228); - padding: 15px; -} - -dl.function dd { - margin-left: 15px; - margin-right: 15px; - margin-top: 5px; - margin-bottom: 15px; -} - -#content dl.function dd h3 { - margin-top: 0px; - margin-left: 0px; - padding-left: 0px; - font-size: 16px; - color: rgb(128, 128, 128); - border-bottom: solid 1px #def; -} - -#content dl.function dd ul, #content dl.function dd ol { - padding: 0px; - padding-left: 15px; - list-style-type: none; -} - -ul.nowrap { - overflow:auto; - white-space:nowrap; -} - -.section-description { - padding-left: 15px; - padding-right: 15px; -} - -/* stop sublists from having initial vertical space */ -ul ul { margin-top: 0px; } -ol ul { margin-top: 0px; } -ol ol { margin-top: 0px; } -ul ol { margin-top: 0px; } - -/* make the target distinct; helps when we're navigating to a function */ -a:target + * { - background-color: #FF9; -} - - -/* styles for prettification of source */ -pre .comment { color: #bbccaa; } -pre .constant { color: #a8660d; } -pre .escape { color: #844631; } -pre .keyword { color: #ffc090; font-weight: bold; } -pre .library { color: #0e7c6b; } -pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } -pre .string { color: #8080ff; } -pre .number { color: #f8660d; } -pre .operator { color: #2239a8; font-weight: bold; } -pre .preprocessor, pre .prepro { color: #a33243; } -pre .global { color: #c040c0; } -pre .user-keyword { color: #800080; } -pre .prompt { color: #558817; } -pre .url { color: #272fc2; text-decoration: underline; } diff --git a/pimp/StringBuffer.lua b/pimp/StringBuffer.lua new file mode 100644 index 0000000..899285c --- /dev/null +++ b/pimp/StringBuffer.lua @@ -0,0 +1,138 @@ +-- +-- Custom string buffer implementation +-- +local config = require('pimp3.config') + +local StringBuffer = {} +StringBuffer.__index = StringBuffer + +-- Кэш для отступов (избегаем повторного создания одинаковых строк) +local indent_cache = {} + +local function getIndent(level) + local size = level * config.indent + + if not indent_cache[size] then + indent_cache[size] = string.rep(' ', size) + end + + return indent_cache[size] +end + +function StringBuffer.new() + return setmetatable({ + buffer = {}, + length = 0 + }, StringBuffer) +end + +function StringBuffer:append(str) + if str == nil then + return self + end + + local s = tostring(str) + self.length = self.length + 1 + self.buffer[self.length] = s + + return self +end + +function StringBuffer:appendLine(str) + if str ~= nil then + self:append(str) + end + self:append('\n') + return self +end + +function StringBuffer:appendIndent(level) + self:append(getIndent(level)) + return self +end + +-- Метод для добавления нескольких строк за раз (оптимизация) +function StringBuffer:appendMultiple(...) + local args = {...} + local n = select('#', ...) + + for i = 1, n do + if args[i] ~= nil then + self.length = self.length + 1 + self.buffer[self.length] = tostring(args[i]) + end + end + + return self +end + +-- Метод для добавления с паддингом (для выравнивания) +function StringBuffer:appendPadded(str, width, align) + local s = tostring(str) + local len = #s:gsub('\27%[[0-9;]*m', '') -- длина без ANSI кодов + local padding = width - len + + if padding > 0 then + if align == 'right' then + self:append(string.rep(' ', padding)) + self:append(s) + elseif align == 'center' then + local left_pad = math.floor(padding / 2) + local right_pad = padding - left_pad + self:append(string.rep(' ', left_pad)) + self:append(s) + self:append(string.rep(' ', right_pad)) + else -- left or default + self:append(s) + self:append(string.rep(' ', padding)) + end + else + self:append(s) + end + + return self +end + +-- Получить текущую длину буфера (количество элементов) +function StringBuffer:size() + return self.length +end + +-- Проверка, пуст ли буфер +function StringBuffer:isEmpty() + return self.length == 0 +end + +function StringBuffer:clear() + -- Более эффективная очистка + for i = 1, self.length do + self.buffer[i] = nil + end + self.length = 0 + return self +end + +function StringBuffer:toString() + return table.concat(self.buffer, '', 1, self.length) +end + +-- Метод для получения последнего добавленного элемента (полезно для отладки) +function StringBuffer:peek() + if self.length > 0 then + return self.buffer[self.length] + end + return nil +end + +-- Метод для удаления последнего элемента +function StringBuffer:pop() + if self.length > 0 then + local value = self.buffer[self.length] + self.buffer[self.length] = nil + self.length = self.length - 1 + return value + end + return nil +end + +return StringBuffer diff --git a/pimp/classes/Boolean.lua b/pimp/classes/Boolean.lua deleted file mode 100755 index 8799eef..0000000 --- a/pimp/classes/Boolean.lua +++ /dev/null @@ -1,44 +0,0 @@ --- Объект прототип boolean типа -local color = require('pimp.color') - -local Boolean = {} -Boolean.__index = Boolean - -function Boolean:new(varname, value) - local obj = {} - obj.type = 'boolean' - obj.varname = varname - obj.value = value - obj.showType = true - obj.colorise = true - obj.color = color.scheme.Boolean - - setmetatable(obj, self) - return obj -end - -function Boolean:compile() - local data = '' - - if self.varname then - data = data..tostring(self.varname)..' = ' - end - - data = data..color(self.color, self.value) - - if self.showType then - data = data..': ['..self.type..']' - end - - return data -end - -function Boolean:setShowType(val) - self.showType = val and true or false - - return self -end - -setmetatable(Boolean, { __call = Boolean.new }) - -return Boolean diff --git a/pimp/classes/Cdata.lua b/pimp/classes/Cdata.lua deleted file mode 100755 index d6605f6..0000000 --- a/pimp/classes/Cdata.lua +++ /dev/null @@ -1,51 +0,0 @@ --- Объект прототип cdata типа -local color = require('pimp.color') - -local Cdata = {} -Cdata.__index = Cdata - -function Cdata:new(varname, value) - local obj = {} - obj.type = 'cdata' - obj.varname = varname - obj.value = value - obj.showType = true - obj.colorise = true - - -- Detect CDATA NULL - if value and value == nil then - obj.color = color.red - else - obj.color = color.scheme.Cdata - end - - setmetatable(obj, self) - return obj -end - -function Cdata:compile() - local data = '' - - if self.varname then - data = data..tostring(self.varname)..' = ' - end - - local value = tostring(self.value) - data = data..'<'..color(self.color, value)..'>' - - if self.showType then - data = data..': ['..self.type..']' - end - - return data -end - -function Cdata:setShowType(val) - self.showType = val and true or false - - return self -end - -setmetatable(Cdata, { __call = Cdata.new }) - -return Cdata diff --git a/pimp/classes/Function.lua b/pimp/classes/Function.lua deleted file mode 100755 index 50181d5..0000000 --- a/pimp/classes/Function.lua +++ /dev/null @@ -1,40 +0,0 @@ --- Объект прототип фукнции -local color = require('pimp.color') - -local Function = {} -Function.__index = Function - -function Function:new(varname, value) - local obj = {} - obj.type = 'function' - obj.varname = varname - obj.value = value - obj.showType = true - obj.colorise = true - obj.color = color.scheme.Function - - setmetatable(obj, self) - return obj -end - -function Function:compile() - local data = '' - - if self.varname then - data = data..tostring(self.varname)..' = ' - end - - data = data..'<'..color(self.color, self.value)..'>' - - return data -end - -function Function:setShowType(val) - self.showType = val and true or false - - return self -end - -setmetatable(Function, { __call = Function.new }) - -return Function diff --git a/pimp/classes/Nil.lua b/pimp/classes/Nil.lua deleted file mode 100755 index 1d36d9f..0000000 --- a/pimp/classes/Nil.lua +++ /dev/null @@ -1,40 +0,0 @@ --- Объект прототип nil типа -local color = require('pimp.color') - -local Nil = {} -Nil.__index = Nil - -function Nil:new(varname) - local obj = {} - obj.type = 'nil' - obj.value = 'nil' - obj.varname = varname - obj.showType = true - obj.colorise = true - obj.color = color.scheme.Nil - - setmetatable(obj, self) - return obj -end - -function Nil:compile() - local data = '' - - if self.varname then - data = data..tostring(self.varname)..' = ' - end - - data = data..color(self.color, self.value) - - return data -end - -function Nil:setShowType(val) - self.showType = val and true or false - - return self -end - -setmetatable(Nil, { __call = Nil.new }) - -return Nil diff --git a/pimp/classes/Number.lua b/pimp/classes/Number.lua deleted file mode 100755 index c340954..0000000 --- a/pimp/classes/Number.lua +++ /dev/null @@ -1,49 +0,0 @@ --- Объект прототип number типа -local color = require('pimp.color') -local config = require('pimp.config') - -local Number = {} -Number.__index = Number - -function Number:new(varname, value) - local obj = {} - obj.type = 'number' - obj.varname = varname - obj.value = value - obj.showType = true - obj.colorise = true - obj.color = color.scheme.Number - - setmetatable(obj, self) - return obj -end - -function Number:compile() - local data = '' - - if self.varname then - data = data..tostring(self.varname)..' = ' - end - - if config.pimp.decimal_to_hexadecimal then - data = data..color(self.color, string.format('0x%X', self.value)) - else - data = data..color(self.color, self.value) - end - - if self.showType then - data = data..': ['..self.type..']' - end - - return data -end - -function Number:setShowType(val) - self.showType = val and true or false - - return self -end - -setmetatable(Number, { __call = Number.new }) - -return Number diff --git a/pimp/classes/String.lua b/pimp/classes/String.lua deleted file mode 100755 index 9b5a4fa..0000000 --- a/pimp/classes/String.lua +++ /dev/null @@ -1,46 +0,0 @@ --- Объект прототип string типа -local color = require('pimp.color') -local stringFormat = require('pimp.string-format') - -local String = {} -String.__index = String - -function String:new(varname, value) - local obj = {} - obj.type = 'string' - obj.varname = varname - obj.value = stringFormat(value) - obj.length = #value - obj.showType = true - obj.colorise = true - obj.color = color.scheme.String - - setmetatable(obj, self) - return obj -end - -function String:compile() - local data = '' - - if self.varname then - data = data..tostring(self.varname)..' = ' - end - - data = data..'"'..self.value..'"' - - if self.showType then - data = data..': ['..color(color.scheme.Number, self.length)..' byte]' - end - - return data -end - -function String:setShowType(val) - self.showType = val and true or false - - return self -end - -setmetatable(String, { __call = String.new }) - -return String diff --git a/pimp/classes/Table.lua b/pimp/classes/Table.lua deleted file mode 100755 index d5ad1ff..0000000 --- a/pimp/classes/Table.lua +++ /dev/null @@ -1,52 +0,0 @@ --- Объект прототип table типа -local color = require('pimp.color') - -local Table = {} -Table.__index = Table - -function Table:new(varname, value) - local obj = {} - obj.type = 'table' - obj.varname = varname - obj.value = value - obj.show_table_addr = false - obj.showType = true - obj.colorise = true - obj.color = color.scheme.Table - - setmetatable(obj, self) - return obj -end - -function Table:compile() - local data = '' - - if self.varname then - if self.show_table_addr then - local adress = color(color.scheme.debugAddress, tostring(self.value)) - data = data - .. color(color.scheme.tablePrefix, tostring(self.varname)) - .. ': <'..adress..'> = ' - else - data = data..color(color.scheme.tablePrefix, tostring(self.varname))..' = ' - end - end - - return data -end - -function Table:setShowType(val) - self.showType = val and true or false - - return self -end - -function Table:setShowTableAddr(val) - self.show_table_addr = val and true or false - - return self -end - -setmetatable(Table, { __call = Table.new }) - -return Table diff --git a/pimp/classes/Thread.lua b/pimp/classes/Thread.lua deleted file mode 100755 index 5b33c7a..0000000 --- a/pimp/classes/Thread.lua +++ /dev/null @@ -1,40 +0,0 @@ --- Объект прототип thread типа -local color = require('pimp.color') - -local Thread = {} -Thread.__index = Thread - -function Thread:new(varname, value) - local obj = {} - obj.type = 'thread' - obj.varname = varname - obj.value = value - obj.showType = true - obj.colorise = true - obj.color = color.scheme.Thread - - setmetatable(obj, self) - return obj -end - -function Thread:compile() - local data = '' - - if self.varname then - data = data..tostring(self.varname)..' = ' - end - - data = data..'<'..color(self.color, self.value)..'>' - - return data -end - -function Thread:setShowType(val) - self.showType = val and true or false - - return self -end - -setmetatable(Thread, { __call = Thread.new }) - -return Thread diff --git a/pimp/classes/Unknown.lua b/pimp/classes/Unknown.lua deleted file mode 100755 index 7c3df07..0000000 --- a/pimp/classes/Unknown.lua +++ /dev/null @@ -1,40 +0,0 @@ --- Объект прототип неопределенного типа -local color = require('pimp.color') - -local Unknown = {} -Unknown.__index = Unknown - -function Unknown:new(varname) - local obj = {} - obj.type = 'unknown' - obj.value = 'unknown' - obj.varname = varname - obj.showType = true - obj.colorise = true - obj.color = color.scheme.Unknown - - setmetatable(obj, self) - return obj -end - -function Unknown:compile() - local data = '' - - if self.varname then - data = data..tostring(self.varname)..' = ' - end - - data = data..color(self.color, self.value) - - return data -end - -function Unknown:setShowType(val) - self.showType = val and true or false - - return self -end - -setmetatable(Unknown, { __call = Unknown.new }) - -return Unknown diff --git a/pimp/classes/Userdata.lua b/pimp/classes/Userdata.lua deleted file mode 100755 index 47e1ee0..0000000 --- a/pimp/classes/Userdata.lua +++ /dev/null @@ -1,45 +0,0 @@ --- Объект прототип userdata типа -local color = require('pimp.color') - -local Userdata = {} -Userdata.__index = Userdata - -function Userdata:new(varname, value) - local obj = {} - obj.type = 'userdata' - obj.varname = varname - obj.value = value - obj.showType = true - obj.colorise = true - obj.color = color.scheme.Userdata - - setmetatable(obj, self) - return obj -end - -function Userdata:compile() - local data = '' - - if self.varname then - data = data..tostring(self.varname)..' = ' - end - - local value = tostring(self.value) - data = data..'<'..color(self.color, value)..'>' - - if self.showType then - data = data..': ['..self.type..']' - end - - return data -end - -function Userdata:setShowType(val) - self.showType = val and true or false - - return self -end - -setmetatable(Userdata, { __call = Userdata.new }) - -return Userdata diff --git a/pimp/color.lua b/pimp/color.lua deleted file mode 100755 index ae9cf22..0000000 --- a/pimp/color.lua +++ /dev/null @@ -1,40 +0,0 @@ --- Модуль для отображения текста в заданном цвете -local config = require('pimp.config') -local colorScheme = require('pimp.colorscheme.default') - -local color = {} - --- Добавление цветов из цветовой схемы --- -for name, col in pairs(colorScheme.color) do - color[name] = col -end - -color.scheme = {} -for name, col in pairs(colorScheme.scheme) do - color.scheme[name] = col -end - -color.log = {} -for name, col in pairs(colorScheme.log) do - color.log[name] = col -end --- - -function color.colorise(value) - config.color.use_color = value and true or false -end - -function color:tocolor(color_type, value) - color_type = color_type or self.white - - if config.color.use_color == false then - return tostring(value) - end - - return color_type..tostring(value)..color.reset -end - -setmetatable(color, { __call = color.tocolor }) - -return color diff --git a/pimp/colors.lua b/pimp/colors.lua new file mode 100755 index 0000000..aab67b3 --- /dev/null +++ b/pimp/colors.lua @@ -0,0 +1,31 @@ +-- +-- Отображения текста в заданном цвете +-- в соответвии с темой +-- +local config = require('pimp3.config') +local scheme = require('pimp3.colorschemes.default') + +local colors = { + reset = scheme.color.reset +} + +colors.scheme = {} +for name, col in pairs(scheme.scheme) do + colors.scheme[name] = col +end + +function colors.colorize(value, color_type) + color_type = color_type or scheme.color.white + + if not config.has_colors then + return tostring(value) + end + + return color_type .. tostring(value) .. colors.reset +end + +setmetatable(colors, { + __call = colors.colorize +}) + +return colors diff --git a/pimp/colorscheme/default.lua b/pimp/colorschemes/default.lua similarity index 80% rename from pimp/colorscheme/default.lua rename to pimp/colorschemes/default.lua index 104535a..35581b5 100644 --- a/pimp/colorscheme/default.lua +++ b/pimp/colorschemes/default.lua @@ -1,6 +1,6 @@ +--- -- Основная цветовая тема (используется 256-цветовая палитра) -- - local color = { reset ="\27[0m", black = "\27[38;5;0m", @@ -46,33 +46,26 @@ local scheme = { Thread = color.brightBlue, Userdata = color.brightBlue, Unknown = color.red, - tablePrefix = color.brightYellow, + nested_table = color.brightCyan, + table_field = color.lightYellow, + table_prefix = color.brightYellow, metatable = color.brightCyan, - cycleTable = color.red, + circular = color.red, address = color.red, - debugAddress = color.brightYellow, - tableBrackets = color.whiite, - emtyTable = color.white, - tableField = color.lightYellow, + debug_address = color.brightYellow, + brackets = color.white, + depth = color.yellow, + emty_table = color.white, visibility = color.blue, controls = color.magenta, escape = "\27[38;5;207m", pattern = "\27[38;5;219m", magic = "\27[38;5;229m", error = color.red, -} - -local log = { - trace = color.cyan, - debug = color.brightBlue, - info = color.green, - warn = color.yellow, - error = color.brightRed, - fatal = color.red, + date = color.lightGreen, } return { color = color, - scheme = scheme, - log = log, + scheme = scheme } diff --git a/pimp/config.lua b/pimp/config.lua index 909f2f1..adda868 100644 --- a/pimp/config.lua +++ b/pimp/config.lua @@ -1,92 +1,50 @@ --- Конфигурационный файл модуля +-- +-- Конфигурационный файл +-- local config = { - -- Основные параметры - pimp = { - prefix = 'p', - separator = ' ➜ ', - -- Ограничения на вхождения, для поиска локальных/глобальных переменных, для печати массива, для вхождений в таблицы - max_seen = 10000, - -- Вывод текста, регулируется :disable, :enable - output = true, - -- Глобальное отключение вывода - global_disable = false, - -- Использовать полные пути до фалов - full_path = true, - -- Обрезать полный путь до заданного регулярного выражения - match_path = '', - -- Флаги отображения - show_visibility = false, - show_type = false, - show_table_addr = false, - show_full_call_stack = true, - -- Отображение лесенкой вывода вызываемых функций - show_call_stack_ladder = true, - -- Отображать числа как hex - decimal_to_hexadecimal = false, - -- Экспериментальные флаги - -- - -- Флаг отвечает за поиск локальных переменных - - -- WARNING: флаг не гарантирует, что найденное имя переменной - - -- будет соответствовать значению этой переменной. - -- Это связано из-за особенностей lua, например полученные varargs - - -- из функции никак не совместить с именем локальной переменой. - -- Точно так же, как и локальные переменные и переменные апвелью с - - -- одинаковыми значениями. - --[[ Пример - local function test(num) - local test = 100 - p(num) - end - test(100) - ]] - find_local_name = false, - }, + -- Отступы + indent = 2, - -- Параметры красивой-печати таблиц - pretty_print = { - -- Символ отступов - tab_char = ' ', - -- Отображать типы - show_type = true, - -- Отображать адреса таблиц - show_table_addr = false, - }, + -- Префикс и разделитель + prefix = 'p', + separator = ' ➜ ', - -- Строковые параметры - string_format = { - -- Экранировать escape последовательность - escape_controls = true, - -- Экранирование всех не ASCII символов - escape_non_ascii = false, - -- Отображать escape в цвете, заданной темой - escape_colorize = true, - -- Классы lua в цвет - patterns_colorize = true, - }, + -- Глубина захода в таблицу + depth = 100, + -- Глубина на вывод массива + depth_array_limit = 100, + -- Максимальное количество элементов выводить из таблицы + -- 0 для вывода всех + max_items = 10, + -- Ограничения на вхождения, для поиска локальных/глобальных переменных, + lookup_limit = 10000, + -- Максимальная длина строки для вывода + max_string_length = 1000, - -- Параметры синхронного логирования - log = { - -- Выходной файл - outfile = 'log.txt', - -- Логировать с учетом цвета - use_color = false, - -- Игнорировать определенные методы логирования - ignore = {}, - -- - match_path = '', - }, + -- Раскрашивать lua паттерны + pattern_colorize = true, + -- Эскейпинг не ascii последовательности (юникод например) + escape_non_ascii = false, + -- Раскрашивать escape последовательности + escape_controls = true, - -- Использование цветовой темы - color = { - use_color = (function() - local term = os.getenv('TERM') - if term and (term == 'xterm' or term:find'-256color$') then - return true - else - return false - end - end), - } + show_address = false, + -- + show_full_call_stack = true, + -- + show_call_stack_ladder = true, + -- + find_local_name = true, + + -- Поддержка цветов + has_colors = (function() + local term = os.getenv('TERM') + if term and (term == 'xterm' or term:find'-256color$') then + return true + end + + return false + end), } return config diff --git a/pimp/constructor.lua b/pimp/constructor.lua deleted file mode 100755 index f248744..0000000 --- a/pimp/constructor.lua +++ /dev/null @@ -1,45 +0,0 @@ --- Конструктор типов -local Number = require('pimp.classes.Number') -local String = require('pimp.classes.String') -local Boolean = require('pimp.classes.Boolean') -local Function = require('pimp.classes.Function') -local Table = require('pimp.classes.Table') -local Thread = require('pimp.classes.Thread') -local Userdata = require('pimp.classes.Userdata') -local Cdata = require('pimp.classes.Cdata') -local Nil = require('pimp.classes.Nil') -local Unknown = require('pimp.classes.Unknown') - -local function constructor(argType, value, argName) - -- Если это cdata NULL - if value and value == nil then - argType = 'cdata' - end - - local obj - if argType == 'number' then - obj = Number(argName, value) - elseif argType == 'boolean' then - obj = Boolean(argName, value) - elseif argType == 'string' then - obj = String(argName, value) - elseif argType == 'function' then - obj = Function(argName, value) - elseif argType == 'table' then - obj = Table(argName, value) - elseif argType == 'thread' then - obj = Thread(argName, value) - elseif argType == 'userdata' then - obj = Userdata(argName, value) - elseif argType == 'cdata' then - obj = Cdata(argName, value) - elseif argType == 'nil' then - obj = Nil(argName) - else - obj = Unknown(argName) - end - - return obj -end - -return constructor diff --git a/pimp/enums/metamethods.lua b/pimp/enums/metamethods.lua deleted file mode 100755 index 0d5b561..0000000 --- a/pimp/enums/metamethods.lua +++ /dev/null @@ -1,43 +0,0 @@ --- Известные метаметоды используемые по умолчанию --- - -local metamethods = { - ["__call"] = true, - ["__concat"] = true, - ["__tostring"] = true, - ["__metatable"] = true, - ["__mode"] = true, - ["__gc"] = true, - - ["__index"] = true, - ["__newindex"] = true, - - ["__add"] = true, - ["__sub"] = true, - ["__mul"] = true, - ["__div"] = true, - ["__pow"] = true, - ["__mod"] = true, - ["__unm"] = true, - - ["__eq"] = true, -- == - ["__lt"] = true, -- < - ["__lе"] = true, -- <= - - ["__len"] = true, - ["__ipairs"] = true, - - -- 5.3 - ["__band"] = true, -- & - ["__bor"] = true, -- | - ["__bxor"] = true, -- ~ - ["__bnot"] = true, -- ~ - ["__shl"] = true, -- << - ["__shr"] = true, -- >> - ["__idiv"] = true, - - -- Tarantool - ["__serialize"] = true, -} - -return metamethods diff --git a/pimp/init.lua b/pimp/init.lua old mode 100755 new mode 100644 index aefa385..b5868eb --- a/pimp/init.lua +++ b/pimp/init.lua @@ -1,593 +1,115 @@ ---- Module for pretty-printing tables and debugging utilities --- @module pimp -local config = require('pimp.config') -local write = require('pimp.write') -local color = require('pimp.color') -local constructor = require('pimp.constructor') -local prettyPrint = require('pimp.pretty-print') -local makePath = require('pimp.utils.makePath') -local plog = require('pimp.log') - ---- Default configuration constants -local DEFAULT_MAX_SEEN = config.pimp.max_seen -local DEFAULT_PREFIX = config.pimp.prefix -local DEFAULT_SEPARATOR = config.pimp.separator - ---- Debug utilities -local getlocal = debug.getlocal -local getinfo = debug.getinfo -local getupvalue = debug.getupvalue -local getfenv = debug.getfenv - ---- Main module table -local pimp = { - prefix = config.pimp.prefix, - separator = config.pimp.separator, - log = plog, - color = color, -} - ---- Find variable name by its value --- @param value The value to find the name for --- @param level Stack level to search in --- @return name, isLocal The variable name and whether it's local -local function findVarName(value, level) - if not config.pimp.find_local_name then - local __type = type(value) - if not ( - __type == 'function' or - __type == 'thread' or - __type == 'table' or - __type == 'userdata' +local config = require('pimp3.config') +local serializeTable = require('pimp3.serializeTable') +local serializeValue = require('pimp3.serializeValue') +local StringBuffer = require('pimp3.StringBuffer') +local colors = require('pimp3.colors') +local term = require('pimp3.term') + +local colorize = colors.colorize + +--- +-- Форматирует и выводит значения с подсветкой синтаксиса +-- @param ... - любые значения для вывода +-- @return ... - возвращает все переданные аргументы (для цепочки вызовов) +local function pimp(...) + local arg_count = select('#', ...) + + -- Если нет аргументов, просто выводим пустую строку + if arg_count == 0 then + io.write('\n') + return + end + + local buffer = StringBuffer.new() + local term_info = term.getInfo() + + -- Определяем, нужно ли использовать цвета + local use_colors = config.colors and term_info.has_colors + + -- Временно отключаем цвета если терминал не поддерживает + local original_colors_setting = config.colors + if not use_colors then + config.colors = false + end + + for i = 1, arg_count do + local arg_value = select(i, ...) + local arg_type = type(arg_value) + + -- Выводим префикс с временем (только если включены цвета) + if use_colors and config.show_timestamp then + buffer:appendMultiple( + '[', + colorize(os.date('%H:%M:%S'), colors.scheme.date), + '] ' ) - then - return nil, nil end - end - - level = level or 2 - local stack = {} - local isLocal = false - -- Search among local variables - local i = 1 - while true do - local name, val = getlocal(level, i) - if name == nil then - break - end - - -- Fix: Compare two cdata in tarantool - local _value = value - if type(val) == 'cdata' then - val = tostring(val) - _value = tostring(value) - end - - if val == _value then - table.insert(stack, name) - end - - i = i + 1 - end - - if stack[1] then - isLocal = true - return stack[#stack], isLocal - end - - -- Search among non-local variables - local func = getinfo(level, 'f').func - - for i = 1, DEFAULT_MAX_SEEN do - local name, val = getupvalue(func, i) - if not name then - break - end - - if val == value then - isLocal = true - return name, isLocal - end - end - - -- Search in environment - local env = nil - - if getfenv then - env = getfenv(level) - elseif _ENV then - env = _ENV - elseif _G then - env = _G - end - - if env ~= nil then - for name, val in pairs(env) do - if val == value then - return name, isLocal + -- Выводим префикс + if config.prefix and config.prefix ~= '' then + buffer:append(config.prefix) + if config.separator and config.separator ~= '' then + buffer:append(config.separator) end end - end - - return nil, isLocal -end - ---- Get function arguments as a string --- @param level Stack level --- @param nparams Number of parameters --- @return String representation of function arguments -local function getFuncArgsStr(level, nparams) - local result = '' - for i = 1, nparams do - local name, value = getlocal(level, i) - if not name then - break - end - - -- Check if argument has a metatable - local __type = type(value) - local __mt = getmetatable(value) - -- Strings are defined as metatables - if __mt and __type ~= 'string' then - __type = 'metatable' - end - if __type == 'string' then - value = tostring(value) - value = '['..value:len()..' byte]' - elseif - __type == 'table' or - __type == 'metatable' or - __type == 'userdata' or - __type == 'cdata' or - __type == 'thread' - then - value = '['..__type..']' + -- Сериализуем значение в зависимости от типа + if arg_type == 'table' then + serializeTable(arg_value, buffer, function(_buffer) + io.write(_buffer:toString()) + _buffer:clear() + end) else - value = tostring(value) + -- Для не-таблиц используем serializeValue + serializeValue(arg_value, buffer) + io.write(buffer:toString()) + buffer:clear() end - result = result..name..': '..value - - if i ~= nparams then - result = result..', ' + -- Добавляем разделитель между аргументами (если их несколько) + if i < arg_count then + io.write('\t') -- или config.args_separator end end - return result -end - ---- Get call stack of functions --- @param level Stack level to start from --- @return Formatted call stack string -local function getCallStack(level) - level = level or 2 - local stack = {} - - for debugLevel = level, DEFAULT_MAX_SEEN do - local info = getinfo(debugLevel) + io.write('\n') + io.flush() -- Важно для корректного вывода в некоторых случаях - if info == nil then - break - end - - if info.name ~= nil then - local funcName = info.name - local funcArgs = '' - local visibilityLabel = '' - - if config.pimp.show_visibility then - if info.namewhat ~= '' then - visibilityLabel = info.namewhat..' ' - end - end - - if info.isvararg then - if info.nparams == 0 then - funcArgs = '(...)' - elseif info.nparams > 0 then - funcArgs = '('..getFuncArgsStr(debugLevel+1, info.nparams)..', ...)' - end - else - if info.nparams == 0 then - funcArgs = '()' - elseif info.nparams > 0 then - funcArgs = '('..getFuncArgsStr(debugLevel+1, info.nparams)..')' - end - end - funcName = funcName..funcArgs - - table.insert(stack, - color(color.scheme.visibility, visibilityLabel)..color(color.brightMagenta, funcName) - ) - end - end - - -- Return only the function from which the call was made - if config.pimp.show_full_call_stack == false then - return stack[2] or '' - end - - -- Restore call sequence excluding pimp call - local result = '' - if config.pimp.show_call_stack_ladder == false then - for i = #stack, 2, -1 do - if i == 2 then - result = result .. stack[i] - else - result = result .. stack[i] .. pimp.separator - end - end - else - result = '' - for i = #stack, 2, -1 do - if i == 2 then - result = result - .. string.rep(' ', #stack - i) - .. ('(%d) ↳ '):format(#stack - i+1) - .. stack[i] - elseif i == #stack then - -- p -> foo/bar.lua:1: [stack count: numer] - -- (1) ↳ local bar(arg: 0) ➜ - result = result - .. ('[stack count: %d]'):format(#stack) - .. string.rep(' ', #stack - i) - .. ('\n(%d) ↳ '):format(#stack - i+1) - .. stack[i] .. pimp.separator - .. '\n' - else - result = result - .. string.rep(' ', #stack - i) - .. ('(%d) ↳ '):format(#stack - i+1) - .. stack[i] .. pimp.separator - .. '\n' - end - end - end - - return result -end - ---- Debug function to print values with context information --- @param ... Values to debug --- @return The input values (for chaining) -function pimp:debug(...) - if not config.pimp.output then - return ... - end - - local level = 2 - local info = getinfo(level, 'lS') - if info == nil then - level = 1 - info = getinfo(level, 'lS') - end - local callpos = makePath(info)..':'..info.currentline - local prefix = self.prefix..self.separator - - local varargsCompiled = {} - for i = 1, select('#', ...) do - local argValue = select(i, ...) - local argType = type(argValue) - local argName, isLocal = findVarName(argValue, level+1) - local obj = constructor(argType, argValue, argName) - - obj:setShowType(config.pimp.show_type) - - local visibilityLabel = '' - if config.pimp.show_visibility then - if isLocal ~= nil then - visibilityLabel = isLocal and 'local ' or 'global ' - visibilityLabel = color(color.scheme.visibility, visibilityLabel) - end - end - - if argType == 'table' then - prettyPrint:setShowType(config.pimp.show_type) - prettyPrint:setShowTableAddr(config.pimp.show_table_addr) - obj:setShowTableAddr(config.pimp.show_table_addr) - - table.insert(varargsCompiled, visibilityLabel..obj:compile()..prettyPrint(argValue)) - else - table.insert(varargsCompiled, visibilityLabel..obj:compile()) - end - end - - local stackStr = getCallStack(level) - local result = table.concat(varargsCompiled, ', ') - - write( - prefix - .. callpos .. ': ' - .. (stackStr == '' and '' or stackStr..': ') - .. result - ) + -- Восстанавливаем настройку цветов + config.colors = original_colors_setting return ... end ---- Print simple text message with timestamp --- @param text Text to print -function pimp.msg(text) - local level = 2 - local info = getinfo(level) - - local linepos = info.currentline - local filename = makePath(info) - - local message = ('[%s] %s:%s %s'):format( - color(color.brightGreen, os.date("%H:%M:%S")), - filename, linepos, - color(color.brightYellow, tostring(text)) - ) - - write(message) -end +-- --- +-- -- Выводит без временной метки и префикса +-- pimp.plain = function(...) +-- local original_prefix = config.prefix +-- local original_timestamp = config.show_timestamp ---- Set prefix and separator --- @param param Table with prefix and separator { prefix='cool', sep='->' } --- @return self for method chaining -function pimp:setPrefix(param) - if param.prefix then - self.prefix = tostring(param.prefix) - end - if param.sep then - self.separator = tostring(param.sep) - end +-- config.prefix = '' +-- config.show_timestamp = false - return self -end +-- pimp(...) ---- Reset prefix and separator to defaults --- @return self for method chaining -function pimp:resetPrefix() - self.prefix = DEFAULT_PREFIX - self.separator = DEFAULT_SEPARATOR +-- config.prefix = original_prefix +-- config.show_timestamp = original_timestamp - return self -end +-- return ... +-- end ---- Disable debug output --- @return self for method chaining -function pimp:disable() - if config.pimp.global_disable then - return self - end +-- --- +-- -- Выводит с кастомным префиксом +-- pimp.with = function(prefix) +-- return function(...) +-- local original_prefix = config.prefix +-- config.prefix = prefix - config.pimp.output = false - return self -end - ---- Enable debug output --- @return self for method chaining -function pimp:enable() - if config.pimp.global_disable then - return self - end - - config.pimp.output = true - return self -end - ---- Globally disable pimp calls --- @return self for method chaining -function pimp:globalDisable() - config.pimp.global_disable = true - config.pimp.output = false - - return self -end - ---- Set path matching pattern --- @param str Lua pattern to match paths --- @return self for method chaining -function pimp:matchPath(str) - config.pimp.match_path = tostring(str) - self.log.match_path = config.pimp.match_path - - return self -end - ---- Disable full path output --- @return self for method chaining -function pimp:disableFullPath() - config.pimp.full_path = false - - return self -end - ---- Enable full path output --- @return self for method chaining -function pimp:enableFullPath() - config.pimp.full_path = true - - return self -end - ---- Disable color output --- @return self for method chaining -function pimp:disableColor() - color.colorise(false) - - return self -end - ---- Enable color output --- @return self for method chaining -function pimp:enableColor() - color.colorise(true) - - return self -end - ---- Enable escaping of non-ASCII characters --- @return self for method chaining -function pimp:enableEscapeNonAscii() - config.string_format.escape_non_ascii = true - - return self -end - ---- Disable escaping of non-ASCII characters --- @return self for method chaining -function pimp:disableEscapeNonAscii() - config.string_format.escape_non_ascii = false - - return self -end - ---- Enable visibility information display --- @return self for method chaining -function pimp:enableVisibility() - config.pimp.show_visibility = true - - return self -end - ---- Disable visibility information display --- @return self for method chaining -function pimp:disableVisibility() - config.pimp.show_visibility = false - - return self -end - ---- Enable type information display --- @return self for method chaining -function pimp:enableType() - config.pimp.show_type = true - - return self -end - ---- Disable type information display --- @return self for method chaining -function pimp:disableType() - config.pimp.show_type = false - - return self -end - ---- Enable table address display --- @return self for method chaining -function pimp:enableTableAddr() - config.pimp.show_table_addr = true - - return self -end - ---- Disable table address display --- @return self for method chaining -function pimp:disableTableAddr() - config.pimp.show_table_addr = false - - return self -end - ---- Enable full call stack display --- @return self for method chaining -function pimp:enableFullCallStack() - config.pimp.show_full_call_stack = true - - return self -end - ---- Disable full call stack display --- @return self for method chaining -function pimp:disableFullCallStack() - config.pimp.show_full_call_stack = false - - return self -end - ---- Return table with pretty-print without respecting output config --- @param t Table to pretty-print --- @return Pretty-printed string representation -function pimp.pp(t) - prettyPrint:setShowType(false) - prettyPrint:setShowTableAddr(false) - return prettyPrint(t) -end - ---- Return table with pretty-print without color and output config --- @param t Table to pretty-print --- @return Pretty-printed string representation without color -function pimp.ppnc(t) - local use_color = config.color.use_color - prettyPrint:setShowType(false) - prettyPrint:setShowTableAddr(false) - color.colorise(false) - local data = prettyPrint(t) - color.colorise(use_color) - return data -end - ---- Get local environment variables --- @param level Stack level to get locals from --- @return Table with local variables -function pimp.getLocalEnv(level) - level = level or 2 - local i = 1 - local env = {} - - while true do - local name, value = getlocal(level, i) - if not name then - break - end - env[name] = value - i = i + 1 - end - - return env -end - ---- Experimental features section - ---- Enable finding local variable names --- @return self for method chaining -function pimp:enableFindLocalName() - config.pimp.find_local_name = true - - return self -end - ---- End section - ---- Disable finding local variable names --- @return self for method chaining -function pimp:disableFindLocalName() - config.pimp.find_local_name = false - - return self -end - ---- Enable call stack ladder display --- @return self for method chaining -function pimp:enableCallStackLadder() - config.pimp.show_call_stack_ladder = true - - return self -end - ---- Disable call stack ladder display --- @return self for method chaining -function pimp:disableCallStackLadder() - config.pimp.show_call_stack_ladder = false - - return self -end - ---- Enable decimal to hexadecimal conversion --- @return self for method chaining -function pimp:decimalToHexadecimal() - config.pimp.decimal_to_hexadecimal = true - - return self -end +-- pimp(...) --- Set metatable to make pimp callable as a function -setmetatable(pimp, { __call = pimp.debug }) +-- config.prefix = original_prefix +-- return ... +-- end +-- end -return pimp \ No newline at end of file +return pimp diff --git a/pimp/inspect.lua b/pimp/inspect.lua new file mode 100644 index 0000000..3c85965 --- /dev/null +++ b/pimp/inspect.lua @@ -0,0 +1,241 @@ +local config = require('pimp3.config') +local colors = require('pimp3.colors') + +local getlocal = debug.getlocal +local getinfo = debug.getinfo +local getupvalue = debug.getupvalue +local getfenv = debug.getfenv +local colorize = colors.colorize + +--- Find variable name by its value +-- @param value The value to find the name for +-- @param level Stack level to search in +-- @return name, isLocal The variable name and whether it's local +local function findVarName(value, level) + if not config.find_local_name then + local __type = type(value) + + if not ( + __type == 'function' or + __type == 'thread' or + __type == 'table' or + __type == 'userdata' + ) + then + return nil, nil + end + end + + level = level or 2 + local stack = {} + local isLocal = false + + -- Search among local variables + do + local i = 1 + while true do + local name, val = getlocal(level, i) + + if name == nil then + break + end + + -- Fix: Compare two cdata in tarantool + local _value = value + + if type(val) == 'cdata' then + val = tostring(val) + _value = tostring(value) + end + + if val == _value then + table.insert(stack, name) + end + + i = i + 1 + end + end + + if stack[1] then + isLocal = true + + return stack[#stack], isLocal + end + + -- Search among non-local variables + local func = getinfo(level, 'f').func + + for i = 1, config.lookup_limit do + local name, val = getupvalue(func, i) + + if not name then + break + end + + if val == value then + isLocal = true + + return name, isLocal + end + end + + -- Search in environment + local env = getfenv and getfenv(level) or _ENV or _G + + if env ~= nil then + for name, val in pairs(env) do + if val == value then + return name, isLocal + end + end + end + + return nil, isLocal +end + +--- Get function arguments as a string +-- @param level Stack level +-- @param nparams Number of parameters +-- @return String representation of function arguments +local function getFuncArgsStr(level, nparams) + local result = '' + + for i = 1, nparams do + local name, value = getlocal(level, i) + + if not name then + break + end + + -- Check if argument has a metatable + local __type = type(value) + local __mt = getmetatable(value) + + -- Strings are defined as metatables + if __mt and __type ~= 'string' then + __type = 'metatable' + end + + if __type == 'string' then + value = tostring(value) + value = '[' .. value:len() .. ' byte]' + elseif + __type == 'table' or + __type == 'metatable' or + __type == 'userdata' or + __type == 'cdata' or + __type == 'thread' + then + value = '[' .. __type .. ']' + else + value = tostring(value) + end + + result = result .. name .. ': ' .. value + + if i ~= nparams then + result = result .. ', ' + end + end + + return result +end + +--- Get call stack of functions +-- @param level Stack level to start from +-- @return Formatted call stack string +local function getCallStack(level) + level = level or 2 + local stack = {} + + for debugLevel = level, config.lookup_limit do + local info = getinfo(debugLevel) + + if info == nil then + break + end + + if info.name ~= nil then + local funcName = info.name + local funcArgs = '' + local visibilityLabel = '' + + if config.show_visibility then + if info.namewhat ~= '' then + visibilityLabel = info.namewhat .. ' ' + end + end + + if info.isvararg then + if info.nparams == 0 then + funcArgs = '(...)' + elseif info.nparams > 0 then + funcArgs = '(' .. getFuncArgsStr(debugLevel + 1, info.nparams) .. ', ...)' + end + else + if info.nparams == 0 then + funcArgs = '()' + elseif info.nparams > 0 then + funcArgs = '(' .. getFuncArgsStr(debugLevel + 1, info.nparams) .. ')' + end + end + + funcName = funcName..funcArgs + + table.insert(stack, + colorize(visibilityLabel, colors.scheme.visibility) + .. colorize(funcName, colors.brightMagenta) + ) + end + end + + -- Return only the function from which the call was made + if config.show_full_call_stack == false then + return stack[2] or '' + end + + -- Restore call sequence excluding pimp call + local result = '' + if config.show_call_stack_ladder == false then + for i = #stack, 2, -1 do + if i == 2 then + result = result .. stack[i] + else + result = result .. stack[i] .. config.separator + end + end + else + result = '' + for i = #stack, 2, -1 do + if i == 2 then + result = result + .. string.rep(' ', #stack - i) + .. ('(%d) ↳ '):format(#stack - i + 1) + .. stack[i] + elseif i == #stack then + -- p -> foo/bar.lua:1: [stack count: numer] + -- (1) ↳ local bar(arg: 0) ➜ + result = result + .. ('[stack count: %d]'):format(#stack) + .. string.rep(' ', #stack - i) + .. ('\n(%d) ↳ '):format(#stack - i + 1) + .. stack[i] .. config.separator + .. '\n' + else + result = result + .. string.rep(' ', #stack - i) + .. ('(%d) ↳ '):format(#stack - i + 1) + .. stack[i] .. config.separator + .. '\n' + end + end + end + + return result +end + +return { + findVarName = findVarName, + getFuncArgsStr = getFuncArgsStr, + getCallStack = getCallStack +} diff --git a/pimp/log.lua b/pimp/log.lua deleted file mode 100755 index 50e3bbd..0000000 --- a/pimp/log.lua +++ /dev/null @@ -1,72 +0,0 @@ --- Модуль для синхронного логирования -local config = require('pimp.config') -local color = require('pimp.color') -local write = require('pimp.write') - -local log = {} - -local models = { - 'trace', - 'debug', - 'info', - 'warn', - 'error', - 'fatal' -} - -local function writeLog(logData) - local fd = io.open(config.log.outfile, 'a') - fd:write(logData) - fd:close() -end - -local function makeLog(logType, message) - -- debug info level = 3 - local info = debug.getinfo(3, 'Sl') - - local logFormat = ('[%s %s] ') - :format(logType, os.date("%H:%M:%S")) - - local path = info.short_src - if config.log.match_path ~= '' then - path = path:match(config.log.match_path) - end - - local colorFormat = color(color.log[logType], logFormat) - local filePos = path..':'..info.currentline..': ' - - if config.log.use_color then - writeLog(colorFormat..filePos..message..'\n') - else - writeLog(logFormat..filePos..message..'\n') - end - - return colorFormat..filePos -end - -local function findIgnore(logType) - for i = 1, #config.log.ignore do - local igonoreType = config.log.ignore[i] - if igonoreType == logType then - return true - end - end - - return false -end - -for i = 1, #models do - local type = models[i] - - log[type] = function(message) - if not config.pimp.output then - return - end - - if not findIgnore(type) then - write(makeLog(type, message)..' '.. message) - end - end -end - -return log diff --git a/pimp/pretty-print.lua b/pimp/pretty-print.lua deleted file mode 100755 index d3fd090..0000000 --- a/pimp/pretty-print.lua +++ /dev/null @@ -1,193 +0,0 @@ ---- Module for pretty-printing tables with color formatting and cycle detection --- @module pretty-print -local config = require('pimp.config') -local color = require('pimp.color') -local constructor = require('pimp.constructor') -local metamethods = require('pimp.enums.metamethods') - ---- Maximum number of table elements to process -local DEFAULT_MAX_SEEN = config.pimp.max_seen - ---- Main module table -local prettyPrint = {} - ---- Check if a table is an array (sequential numeric keys) -local function isArray(tbl) - if type(tbl) ~= 'table' then - return false, nil - end - - local index = 0 - for _,_ in next, tbl do - index = index + 1 - - if tbl[index] == nil then - return false, nil - end - end - - return true, index -end - ---- Check if a value has a metatable -local function ismt(t) - return getmetatable(t) ~= nil -end - ---- Get appropriate prefix for table type -local function tabletypePrefix(t) - return ismt(t) - and color(color.scheme.metatable, 'metatable') - or color(color.scheme.tablePrefix, 'table') -end - ---- Wrap an object for pretty printing --- @param obj Any object to be pretty-printed --- @return string Formatted string representation of the object -function prettyPrint:wrap(obj, indent, seen, seen_count) - local _type = type(obj) - indent = indent or 0 - seen = seen or {} - seen_count = seen_count or 0 - - if _type == 'nil' then - return constructor('nil', obj) - :setShowType(config.pretty_print.show_type) - :compile() - end - - if _type == 'table' then - -- Check if we've already seen this table - if seen[obj] then - local address = tostring(obj) - if address:find('table: ') then - address = address:match('table: (.+)') - end - return '<'..color(color.scheme.cycleTable, 'cycle: '..address)..'>' - end - seen[obj] = true - - -- Detect empty table - if not next(obj) then - -- Print table type - if config.pretty_print.show_type then - return color(color.scheme.emtyTable, ('{}: [%s]'):format(tabletypePrefix(obj))) - end - return color(color.scheme.emtyTable, '{}') - end - - local _result = color(color.scheme.tableBrackets, '{\n') - for key, val in pairs(obj) do - seen_count = seen_count + 1 - - if seen_count >= DEFAULT_MAX_SEEN then - local error = 'Seen overflow. Elements count: ' .. tostring(#obj) - - _result = _result - .. string.rep(config.pretty_print.tab_char, indent + 2) - .. '<'..color(color.scheme.error, 'Warning: '..tostring(error))..'>' - .. '\n' - - break - end - - local key_type = type(key) - - -- Detect table - local valIsTable = type(val) == 'table' - - -- Detect if key is number - local fieldType - if key_type == 'string' and tonumber(key) then - fieldType = '["%s"]' - elseif key_type == 'number' then - fieldType = '[%s]' - else - fieldType = '%s' - end - - -- Field color - local fieldColor = color.scheme.tableField - - if metamethods[key] then - fieldColor = color.scheme.metatable - end - - if config.pretty_print.show_table_addr and valIsTable then - local fmt_str = '%s' - .. fieldType - ..': <' .. color(color.scheme.debugAddress, '%s') .. '> = ' - - _result = _result - .. fmt_str:format( - string.rep(config.pretty_print.tab_char, indent + 2), -- Space - color(fieldColor, key), -- Field name - tostring(val) -- Table address - ) - else - local fmt_str = '%s'..fieldType..' = ' - - _result = _result - .. fmt_str:format( - string.rep(config.pretty_print.tab_char, indent + 2), -- Space - color(fieldColor, key) -- Field name - ) - end - - local success, error = pcall(function() - return self:wrap(val, indent + 2, seen, seen_count) - end) - - if not success then - error = '<'..color(color.scheme.error, 'error: '..tostring(error))..'>' - end - - _result = _result..error..',\n' - end - - local labelType = '' - if config.pretty_print.show_type then - local isArr, arrCount = isArray(obj) - if isArr then - labelType = labelType..': [array '..color(color.scheme.Number, arrCount)..']' - end - labelType = labelType..(': [%s]'):format(tabletypePrefix(obj)) - end - - _result = _result - .. string.rep(config.pretty_print.tab_char, indent) - .. color(color.scheme.tableBrackets, '}') - .. labelType - - return _result - end -- if table - - return constructor(_type, obj) - :setShowType(config.pretty_print.show_type) - :compile() -end - ---- Set whether to show type information --- @param val Boolean value to enable/disable type display --- @return self for method chaining -function prettyPrint:setShowType(val) - config.pretty_print.show_type = val and true or false - - return self -end - ---- Set whether to show table addresses --- @param val Boolean value to enable/disable table address display --- @return self for method chaining -function prettyPrint:setShowTableAddr(val) - config.pretty_print.show_table_addr = val and true or false - - return self -end - ---- Set metatable to make prettyPrint callable as a function -setmetatable(prettyPrint, { - __call = prettyPrint.wrap, -}) - -return prettyPrint diff --git a/pimp/serializeTable.lua b/pimp/serializeTable.lua new file mode 100644 index 0000000..bd7e4fd --- /dev/null +++ b/pimp/serializeTable.lua @@ -0,0 +1,196 @@ +local config = require('pimp3.config') +local colors = require('pimp3.colors') +local serializeValue = require('pimp3.serializeValue') +local term = require('pimp3.term') + +local colorize = colors.colorize +local c_circular = colors.scheme.circular + +-- Функция для оценки визуальной длины значения (без ANSI escape codes) +local function getValueLength(str) + -- Убираем ANSI escape sequences для подсчета реальной длины + local clean = tostring(str):gsub('\27%[[0-9;]*m', '') + return #clean +end + +-- Сериализация значения во временный буфер для измерения длины +local function serializeToString(value, serializeValueFunc) + local StringBuffer = require('pimp3.StringBuffer') + local temp_buffer = StringBuffer.new() + serializeValueFunc(value, temp_buffer) + return temp_buffer:toString() +end + +local function serializeTable(tbl, buffer, callback, level, visited) + level = level or 0 + visited = visited or {} + + if level >= config.depth then + buffer:append(colorize('< ... Out of depth >', colors.depth)) + return + end + + if visited[tbl] then + buffer:append(colorize('', c_circular)) + return + end + + visited[tbl] = true + + if not next(tbl) then + buffer:append('{}') + return + end + + buffer:appendMultiple('{', '\n') + + local arr_len = #tbl + local is_array = arr_len > 0 + + if is_array then + local term_width = term.getSize() + local indent = (level + 1) * config.indent + + -- Собираем все элементы массива и вычисляем их длины + local items = {} + local max_length = 0 + local display_limit = math.min(arr_len, config.depth_array_limit) + + for i = 1, display_limit do + local value = tbl[i] + if value ~= nil then + local item_str = serializeToString(value, serializeValue) + local visual_length = getValueLength(item_str) + + table.insert(items, { + str = item_str, + length = visual_length + }) + + if visual_length > max_length then + max_length = visual_length + end + end + end + + -- Вычисляем сколько элементов помещается в строку с учетом выравнивания + local item_width = max_length + 2 -- +2 для ", " + local items_per_line = math.max(1, math.floor((term_width - indent) / item_width)) + + -- Проверяем наличие не-числовых ключей + local has_non_numeric = false + for key in pairs(tbl) do + if type(key) ~= 'number' then + has_non_numeric = true + break + end + end + + -- Выводим элементы с выравниванием + for i, item in ipairs(items) do + if (i - 1) % items_per_line == 0 then + buffer:appendIndent(level + 1) + end + + -- Используем новый метод appendPadded для выравнивания + buffer:appendPadded(item.str, max_length, 'right') + buffer:append(', ') + + -- Перенос строки после каждых items_per_line элементов + if i % items_per_line == 0 or i == #items then + buffer:append('\n') + end + end + + -- Показываем сообщение о пропущенных элементах + if display_limit < arr_len then + buffer:appendIndent(level + 1) + buffer:append(string.format('< ... %d more items >', arr_len - display_limit)) + buffer:append('\n') + end + + -- Обработка не-числовых ключей + if has_non_numeric then + for key, value in pairs(tbl) do + if type(key) ~= 'number' then + buffer:appendIndent(level + 1) + serializeValue(key, buffer) + buffer:appendMultiple(' = ') + serializeValue(value, buffer) + buffer:appendMultiple(', ', '\n') + end + end + end + else + -- Обработка обычных таблиц (хэш-таблиц) + for key, value in pairs(tbl) do + buffer:appendIndent(level + 1) + + local key_type = type(key) + + if key_type == 'number' then + buffer:appendMultiple( + '[', + colorize(key, colors.scheme.Number), + ']' + ) + elseif key_type == 'string' and key:find('%s') then + buffer:appendMultiple( + '["', + colorize(key, colors.scheme.table_field), + '"]' + ) + else + buffer:append(colorize(key, colors.scheme.table_field)) + end + + buffer:append(' = ') + + local value_type = type(value) + + if value_type == 'table' then + local nestedTable = tbl[key] + local itemCount = 0 + local showTable = true + + if config.max_items > 0 then + for _, _ in pairs(nestedTable) do + itemCount = itemCount + 1 + + if itemCount > config.max_items then + showTable = false + break + end + end + end + + if showTable then + callback(buffer) + serializeTable(tbl[key], buffer, callback, level + 1, visited) + else + if visited[value] then + local msg = config.show_address + and string.format('', value) + or '' + buffer:append(colorize(msg, c_circular)) + else + serializeTable(tbl[key], buffer, callback, level + 1, visited) + end + end + else + serializeValue(value, buffer, { table = false, level = level + 1 }) + end + + buffer:appendMultiple(',', '\n') + end + end + + buffer:appendIndent(level) + buffer:append('}') + + visited[tbl] = nil + + callback(buffer) +end + +return serializeTable diff --git a/pimp/serializeValue.lua b/pimp/serializeValue.lua new file mode 100644 index 0000000..485428d --- /dev/null +++ b/pimp/serializeValue.lua @@ -0,0 +1,104 @@ +--- +-- Value serializer +--- +local stringFormat = require('pimp3.stringFormat') +local StringBuffer = require('pimp3.StringBuffer') +local colors = require('pimp3.colors') +local config = require('pimp3.config') +local inspect = require('pimp3.inspect') +local findVarName = inspect.findVarName + +local colorize = colors.colorize + +local function serializeValue(value, buffer, opts) + buffer = buffer or StringBuffer.new() + + local value_type = type(value) + + -- TODO: Можем ли проверить, что интерпритатор luajit? + -- Если это cdata NULL + if value and value == nil then + value_type = 'Cdata' + end + + if value_type == 'nil' then + buffer:append(colorize('nil', colors.scheme.Nil)) + + elseif value_type == 'table' then + if opts and opts.table == false then + return buffer + end + + local is_empty = next(value) == nil + + if is_empty then + buffer:append(colorize('{}', colors.scheme.emty_table)) + else + if config.show_address then + buffer:append(colorize((''):format(value), colors.scheme.nested_table)) + else + buffer:append(colorize('
', colors.scheme.nested_table)) + end + end + + elseif value_type == 'boolean' then + buffer:append(colorize(tostring(value), colors.scheme.Boolean)) + + elseif value_type == 'number' then + if value == 1/0 then + return buffer:append(colorize('', colors.scheme.Number)) + elseif value == -1/1 then + return buffer:append(colorize('<-INF>', colors.scheme.Number)) + elseif value ~= value then + return buffer:append(colorize('', colors.scheme.Number)) + end + + buffer:append(colorize(tostring(value), colors.scheme.Number)) + + elseif value_type == 'string' then + local len = #value + + if len > config.max_string_length then + value = string.sub(value, 1, config.max_string_length) .. '...' + end + + buffer:append('"' .. stringFormat(value) .. '"') + + elseif value_type == 'function' then + buffer:append(colorize('', colors.scheme.Function)) + + elseif value_type == 'thread' then + if config.show_address then + buffer:append(colorize((''):format(value), colors.scheme.Thread)) + else + buffer:append(colorize('', colors.scheme.Thread)) + end + + elseif value_type == 'userdata' then + local fmt = '' + buffer:append(colorize(fmt, colors.scheme.Userdata)) + + else + buffer:append(colorize('<' .. value_type .. '>', colors.scheme.Unknown)) + end + + return buffer +end + +return serializeValue diff --git a/pimp/string-format.lua b/pimp/string-format.lua deleted file mode 100755 index dfd903a..0000000 --- a/pimp/string-format.lua +++ /dev/null @@ -1,150 +0,0 @@ -local config = require('pimp.config') -local color = require('pimp.color') - -local magics = { - ['-'] = true, - ['%'] = true, - ['('] = true, - [')'] = true, - ['.'] = true, - ['+'] = true, - ['*'] = true, - ['?'] = true, - ['['] = true, - [']'] = true, - ['^'] = true, - ['$'] = true, -} - -local patterns = { - [65] = 'A', - [66] = 'B', - [67] = 'C', - [68] = 'D', - [70] = 'F', - [71] = 'G', - [76] = 'L', - [80] = 'P', - [83] = 'S', - [85] = 'U', - [87] = 'W', - [97] = 'a', - [98] = 'b', - [99] = 'c', - [100] = 'd', - [102] = 'f', - [103] = 'g', - [108] = 'l', - [112] = 'p', - [115] = 's', - [117] = 'u', - [119] = 'w', -} - -local special = { - [7] = 'a', - [8] = 'b', - [9] = 't', - [10] = 'n', - [11] = 'v', - [12] = 'f', - [13] = 'r', -} - -local controls = {} -for i = 0, 31 do - local c = special[i] - if not c then - if i < 10 then - c = '00' .. tostring(i) - else - c = '0' .. tostring(i) - end - end - - controls[i] = tostring('\\' .. c) -end - --- 127 DEL -controls[127] = tostring('\\127') --- 34 " -controls[34] = tostring('\\"') - -local function escape_colorize(char, colorize) - local result = '' - if colorize then - result = result .. (config.color.use_color and color.reset or '') - result = result .. color(color.scheme.controls, char) - result = result .. (config.color.use_color and color.scheme.String or '') - return result - end - - return char -end - -local function pattern_colorize(str, cur_pos, char, colorize) - if colorize then - local result = '' - - -- Поиск паттернов - -- %w, %a, ... - local prev_byte = string.byte(str, cur_pos-1) - local cur_byte = string.byte(str, cur_pos) - -- 37 % - if prev_byte == 37 then - if patterns[cur_byte] then - result = result .. (config.color.use_color and color.reset or '') - result = result .. color(color.scheme.pattern, char) - result = result .. (config.color.use_color and color.scheme.String or '') - return result - end - end - - local next_byte = string.byte(str, cur_pos+1) - if magics[char] then - local color_type = color.scheme.magic - -- 37 % - if cur_byte == 37 and patterns[next_byte] then - color_type = color.scheme.pattern - end - - result = result .. (config.color.use_color and color.reset or '') - result = result .. color(color_type, char) - result = result .. (config.color.use_color and color.scheme.String or '') - return result - end - end - - return char -end - -local function string_format(str) - local result = (config.color.use_color and color.scheme.String or '') - for i = 1, #str do - local char = string.sub(str, i, i) - local byte = string.byte(char, 1) - - -- Экранирование спец.символов - if config.string_format.escape_controls then - if byte < 128 then - if controls[byte] then - result = result .. escape_colorize(controls[byte], config.string_format.escape_colorize) - else - result = result .. pattern_colorize(str, i, char, config.string_format.patterns_colorize) - end - elseif byte < 256 then - if config.string_format.escape_non_ascii then - result = result .. escape_colorize('\\'..byte, true) - else - result = result .. char - end - end - end - end - - result = result .. (config.color.use_color and color.reset or '') - - return result -end - -return string_format diff --git a/pimp/stringFormat.lua b/pimp/stringFormat.lua new file mode 100755 index 0000000..d405a80 --- /dev/null +++ b/pimp/stringFormat.lua @@ -0,0 +1,174 @@ +local config = require('pimp3.config') +local colors = require('pimp3.colors') + +local colorize = colors.colorize + +local magics = { + ['-'] = true, + ['%'] = true, + ['('] = true, + [')'] = true, + ['.'] = true, + ['+'] = true, + ['*'] = true, + ['?'] = true, + ['['] = true, + [']'] = true, + ['^'] = true, + ['$'] = true +} + +local patterns = { + [65] = 'A', + [66] = 'B', + [67] = 'C', + [68] = 'D', + [70] = 'F', + [71] = 'G', + [76] = 'L', + [80] = 'P', + [83] = 'S', + [85] = 'U', + [87] = 'W', + [97] = 'a', + [98] = 'b', + [99] = 'c', + [100] = 'd', + [102] = 'f', + [103] = 'g', + [108] = 'l', + [112] = 'p', + [115] = 's', + [117] = 'u', + [119] = 'w' +} + +local special = { + [7] = 'a', + [8] = 'b', + [9] = 't', + [10] = 'n', + [11] = 'v', + [12] = 'f', + [13] = 'r' +} + +local controls = {} +for i = 0, 31 do + local c = special[i] + if not c then + if i < 10 then + c = '00' .. tostring(i) + else + c = '0' .. tostring(i) + end + end + + controls[i] = tostring('\\' .. c) +end + +-- 127 DEL +controls[127] = tostring('\\127') +-- 34 " +controls[34] = tostring('\\"') + +local function escapeColorize(char) + local result = '' + + if config.escape_colorize then + result = result .. (config.has_colors and colors.reset or '') + result = result .. colorize(char, colors.scheme.controls) + result = result .. (config.has_colors and colors.scheme.String or '') + + return result + end + + return char +end + +local function patternColorize(str, cur_pos, char) + if config.pattern_colorize == false then + return char + end + + local result = '' + + -- Поиск паттернов + -- %w, %a, ... + local prev_byte = string.byte(str, cur_pos - 1) + local cur_byte = string.byte(str, cur_pos) + + -- 37 % + if prev_byte == 37 then + if patterns[cur_byte] then + result = result .. (config.has_colors and colors.reset or '') + result = result .. colorize(char, colors.scheme.pattern) + result = result .. (config.has_colors and colors.scheme.String or '') + + return result + end + end + + local next_byte = string.byte(str, cur_pos + 1) + if magics[char] then + local color_type = colors.scheme.magic + -- 37 % + if cur_byte == 37 and patterns[next_byte] then + color_type = colors.scheme.pattern + end + + result = result .. (config.has_colors and colors.reset or '') + result = result .. colorize(char, color_type) + result = result .. (config.has_colors and colors.scheme.String or '') + + return result + end + + return char +end + +local function stringFormat(str) + local result = (config.has_colors and colors.scheme.String or '') + + -- Экранирование специальных символов + if config.escape_controls then + for i = 1, #str do + local char = string.sub(str, i, i) + local byte = string.byte(char, 1) + + if byte < 128 then + if controls[byte] then + result = result .. escapeColorize(controls[byte]) + else + result = result .. patternColorize(str, i, char) + end + + elseif byte < 256 then + if config.escape_non_ascii then + result = result .. escapeColorize('\\' .. byte, true) + else + result = result .. char + end + end + end + elseif config.escape_non_ascii then + for i = 1, #str do + local char = string.sub(str, i, i) + local byte = string.byte(char, 1) + + if byte > 128 and byte < 256 then + result = result .. escapeColorize('\\' .. byte, true) + else + result = result .. char + end + end + else + result = result .. str + end + + result = result .. (config.has_colors and colors.reset or '') + + return result +end + +return stringFormat diff --git a/pimp/term.lua b/pimp/term.lua new file mode 100644 index 0000000..d752740 --- /dev/null +++ b/pimp/term.lua @@ -0,0 +1,77 @@ +--- +-- +local ffi = require('ffi') + +ffi.cdef[[ + typedef struct { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; + } winsize; + + int ioctl(int fd, unsigned long request, ...); +]] + +local DEFAULT_COLS = 80 +local DEFAULT_ROWS = 20 + +-- IOCTL коды для разных ОС +local TIOCGWINSZ = { + linux = 0x5413, + macos = 0x40087468, + bsd = 0x40087468, + windows = 0x5413, + unknown = 0x5413 +} + +local function getTIOCGWINSZ() + local __os = string.lower(jit.os) + return TIOCGWINSZ[__os] or TIOCGWINSZ.unknown +end + +local term = {} + +--- +-- @return[1] cols +-- @return[2] rows +function term.getSize() + local TIOCGWINSZ = getTIOCGWINSZ() + local ws = ffi.new("winsize") + + if ffi.C.ioctl(1, TIOCGWINSZ, ws) == 0 then + return tonumber(ws.ws_col), tonumber(ws.ws_row) + end + + return DEFAULT_COLS, DEFAULT_ROWS +end + +function term.getColorSupport() + local term = os.getenv("TERM") + + if term == nil then + return false + end + + local term_str = term:lower() + if term_str == "dumb" then + return false + end + + if term_str:match("256color") then + return true + end +end + +function term.getInfo() + local cols, rows = term.getSize() + local has_colors = term.getColorSupport() + + return { + cols = cols, + rows = rows, + has_colors = has_colors, + } +end + +return term diff --git a/pimp/utils/makePath.lua b/pimp/utils/makePath.lua deleted file mode 100755 index f0b97db..0000000 --- a/pimp/utils/makePath.lua +++ /dev/null @@ -1,20 +0,0 @@ -local config = require('pimp.config') - -local function makePath(info) - local filename = info and info.short_src or '' - - if config.pimp.full_path == false then - filename = filename:match('.+/(.-)$') - else - if config.pimp.match_path ~= '' then - local matchPath = filename:match(config.pimp.match_path) - if matchPath then - filename = matchPath - end - end - end - - return filename -end - -return makePath diff --git a/pimp/write.lua b/pimp/write.lua deleted file mode 100755 index ba7794d..0000000 --- a/pimp/write.lua +++ /dev/null @@ -1,28 +0,0 @@ ---- --- Writes the given arguments to the standard output stream, followed by a newline --- character, and flushes the output --- --- @function write --- @param ... - The data to be written to the output --- --- @usage --- write("Hello, world!") --- -local function write(...) - local data = '' - local argsCount = select('#', ...) - - for i = 1, argsCount do - local arg = select(i, ...) - if arg == '' then - arg = '\'\'' - end - - data = data..tostring(arg)..'\t' - end - - io.write(data, '\n') - io.flush() -end - -return write diff --git a/screenshots/pimp_logo.png b/screenshots/pimp_logo.png deleted file mode 100644 index f7fbf0c..0000000 Binary files a/screenshots/pimp_logo.png and /dev/null differ diff --git a/screenshots/screenshot.png b/screenshots/screenshot.png deleted file mode 100644 index 4a0c477..0000000 Binary files a/screenshots/screenshot.png and /dev/null differ diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..2765783 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,2 @@ +# TODO + - Tests diff --git a/tests/all_test.lua b/tests/all_test.lua deleted file mode 100755 index 5b25a46..0000000 --- a/tests/all_test.lua +++ /dev/null @@ -1,196 +0,0 @@ -local p = require('pimp.init') --- p:disable() -- Disable debug output -:enableType() -- Enable show type -:enableTableAddr() -- Enable show table address -:enableVisibility() -- local / global / .. - --- --- Inspect Variables --- -p('Pimp Module!') -p(true, false, nil) -p(function() end) -p(io.stderr) -p(10000, math.pi) -p(0/0, -1/0, 1/0) - -local test = function () end -p(function() end, test) - -local co = coroutine.create(function() end) -p(co) - -if box then - p(box.NULL) -end - --- --- Change prefix test --- -p:setPrefix({ prefix = 'INFO', sep = '|-> ' }) -p('Wow! It\'s new prefix!') -p:resetPrefix() - --- --- Disable color test --- -p:disableColor() -p('String without colors') -p:enableColor() - --- --- Disable test --- -p:disable() -p('Hello?') -p:enable() - --- --- Inspect Functions --- -local function foo(t) - return t, true -end - -p( - foo({ - 'apple', 'banana', 'orange' - }) -) - -local function mv(a, b, c) - p('Message from local func') - local NAME = 'uriid1' - p(NAME) - p(a, b, c) - - if box then - local cdata = box.NULL - p(cdata) - end - - return a, b, c -end -p(mv(1, 2, 3)) - -local _ = (function(...) - p(...) - return true -end)(4, 5, 6) - -local function infunc(a, b) - p(a, b) - return a + b -end -p(infunc(10, 5)) - -local function func(arg1, ...) - return p(arg1, ...) -end -func(1, '2', {}) - -local function funcVararg(...) - p(...) -end -funcVararg(1, 2, 3) - -local function tt(t1, t2, t3) - p:disableVisibility() - p(t1, t2, t3) - p:enableVisibility() -end - -tt(t1, t2, {}) - -local t1 = {} -setmetatable(t1, { - __add = function() end, - __sub = function() end, - __mul = function() end, -}) - -local t2 = {1, 2, 3} -setmetatable(t2, { - __tostring = function() - return 999 - end -}) - -p(getmetatable(t1)) - --- -local function test_1(a, ...) p(); return a, ... end -local function test_2(b, ...) p(); return b, ... end -local function test_3(c, ...) p(); return c, ... end - -test_1('foo', p(test_1), p(test_2, test_2), 'baz', p(test_3)) - --- --- Inspect local variables --- -local function test_get_local_env(arg) - local name = 'uriid1' - local age = 12 - local env = p.getLocalEnv(2) - p(env) -end -test_get_local_env('Hello :)') - --- --- Inspect Tables --- -local arrTest = { - [-99] = 'Array?' -} -p(arrTest) - -local realArr = { - [1] = 100, - [2] = 200, - [3] = 300, -} -p(realArr) - -local table_name = { - message = { - chat = { - title = 'Кто съел мороженое?', - } - }, - - ['array'] = { - "apple.", 'banana', 'orange', 'green' - }, - - inf = 1/0, - nan = 0/0, - boolean = true, - string = '^\t(Hello)%.[\r\n]world!\r\n$', - pattern = '^([a-z0-9]+)@([%w%d]+)$', - func = function() end, - thread = coroutine.create(function() end), - empty_table = {}, - NULL = box and box.NULL or ':)' -} -table_name.recursive = table_name - -p(table_name) - --- --- Inspect Tuple --- -if box and box.tuple then - local tuple = box.tuple.new {'foo', 'bar', 'baz'} - p(tuple) -end - --- --- Log --- -p.log.ignore = {'info', 'warn'} -p.log.trace(('Trace %s'):format 'message') -p.log.debug('Debug message') -p.log.info('Info message') -p.log.warn('Warn message') -p.log.error('Error message') -p.log.fatal('Fatal message') diff --git a/tests/big_array.lua b/tests/big_array.lua deleted file mode 100644 index ff1e804..0000000 --- a/tests/big_array.lua +++ /dev/null @@ -1,12 +0,0 @@ -local p = require('pimp') -:enableType() -:enableTableAddr() -:enableVisibility() - - -local arr = {} -for i = 3000, 90000 do - arr[i] = 0 -end - -p(arr) diff --git a/tests/ffi_test.lua b/tests/ffi_test.lua deleted file mode 100644 index 15afc40..0000000 --- a/tests/ffi_test.lua +++ /dev/null @@ -1,23 +0,0 @@ -local p = require('pimp') -:enableType() -:enableTableAddr() -:enableVisibility() -:enableFindLocalName() - -local ffi = require('ffi') -local arr = ffi.new('int[5]') -p(arr) - -local null = ffi.new('void*') -p(null) - -local num1 = 10ULL -local num2 = 10LL -local num3 = 2^64 - 1 -local num4 = 0xFFFULL -local num5 = 12.56i -p(num1) -p(num2) -p(num3) -p(num4) -p(num5) diff --git a/tests/main_mod/init.lua b/tests/main_mod/init.lua deleted file mode 100644 index e85fe5f..0000000 --- a/tests/main_mod/init.lua +++ /dev/null @@ -1,9 +0,0 @@ -local pipe = require('main_mod.middleware') - -local function test() - local module = require('main_mod.module_route.init') - local res, _ = pcall(module, 'Hello!!!') - return res -end - -return pipe(test) diff --git a/tests/main_mod/middleware.lua b/tests/main_mod/middleware.lua deleted file mode 100644 index c056176..0000000 --- a/tests/main_mod/middleware.lua +++ /dev/null @@ -1,11 +0,0 @@ -local function pipe(...) - local handlers = {...} - - return function (text) - for _, handler in ipairs(handlers) do - return handler(text) - end - end -end - -return pipe diff --git a/tests/main_mod/module_route/init.lua b/tests/main_mod/module_route/init.lua deleted file mode 100644 index eeac95b..0000000 --- a/tests/main_mod/module_route/init.lua +++ /dev/null @@ -1,7 +0,0 @@ -local p = require('pimp') - -function route (res) - p(res) -end - -return route diff --git a/tests/return_test.lua b/tests/return_test.lua deleted file mode 100644 index 1f48448..0000000 --- a/tests/return_test.lua +++ /dev/null @@ -1,15 +0,0 @@ -local p = require('pimp.init') --- p:disable() -- Disable debug output -:enableType() -- Enable show type -:enableTableAddr() -- Enable show table address -:enableVisibility() -- local / global / .. - -local function foo() - return 'bar' -end - -if true then - local result = foo() - p(result) - return p(result) -end diff --git a/tests/test_call_stack.lua b/tests/test_call_stack.lua deleted file mode 100644 index 85eb60e..0000000 --- a/tests/test_call_stack.lua +++ /dev/null @@ -1,24 +0,0 @@ -local p = require('pimp') - :enableType() - :enableTableAddr() - :enableVisibility() - :enableCallStackLadder() - -local test_1 = function(arg) - arg = arg + 1 - local test_2 = function(arg) - arg = arg + 1 - local test_3 = function(arg) - arg = arg + 1 - local test_4 = function(arg) - arg = arg + 1 - p('Stack test') - end - test_4(arg) - end - test_3(arg) - end - test_2(arg) -end - -test_1(0) diff --git a/tests/test_module.lua b/tests/test_module.lua deleted file mode 100644 index 3e436fe..0000000 --- a/tests/test_module.lua +++ /dev/null @@ -1,2 +0,0 @@ -local moodule = require('main_mod.init') -moodule()