mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
03b1e8728b
commit
528e4a6130
11 changed files with 110 additions and 118 deletions
|
|
@ -18,7 +18,6 @@ mod which;
|
||||||
use app::*;
|
use app::*;
|
||||||
use executor::*;
|
use executor::*;
|
||||||
use logs::*;
|
use logs::*;
|
||||||
use parser::*;
|
|
||||||
use root::*;
|
use root::*;
|
||||||
use signals::*;
|
use signals::*;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use config::theme::Color;
|
use config::theme::Color;
|
||||||
use ratatui::{prelude::{Buffer, Rect}, style::{Modifier, Style}, text::{Line, Span}, widgets::{Paragraph, Widget}};
|
use ratatui::{prelude::{Alignment, Buffer, Rect}, style::{Modifier, Style}, text::{Line, Span}, widgets::{Paragraph, Widget}};
|
||||||
|
|
||||||
pub struct Parser;
|
pub struct Parser;
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ impl Parser {
|
||||||
Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::<Vec<_>>())
|
Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn area(args: Vec<&str>) -> Result<Rect> {
|
fn area(args: &[&str]) -> Result<Rect> {
|
||||||
Ok(Rect {
|
Ok(Rect {
|
||||||
x: args[0].parse()?,
|
x: args[0].parse()?,
|
||||||
y: args[1].parse()?,
|
y: args[1].parse()?,
|
||||||
|
|
@ -105,13 +105,24 @@ impl Parser {
|
||||||
};
|
};
|
||||||
|
|
||||||
let args: Vec<_> = args.split(',').collect();
|
let args: Vec<_> = args.split(',').collect();
|
||||||
if args.len() != 4 {
|
if args.len() != 5 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(area) = Self::area(args) {
|
let Ok(area) = Self::area(&args) else {
|
||||||
Self::paragraph(content).render(area, buf);
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut paragraph = Self::paragraph(content);
|
||||||
|
if let Ok(align) = args[4].parse::<u8>() {
|
||||||
|
paragraph = paragraph.alignment(match align {
|
||||||
|
1 => Alignment::Center,
|
||||||
|
2 => Alignment::Right,
|
||||||
|
_ => Alignment::Left,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paragraph.render(area, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use core::Ctx;
|
use core::Ctx;
|
||||||
|
|
||||||
use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::Widget};
|
use ratatui::{buffer::Buffer, prelude::Rect, widgets::Widget};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::parser::Parser;
|
use crate::parser::Parser;
|
||||||
|
|
@ -15,12 +15,7 @@ impl<'a> Layout<'a> {
|
||||||
|
|
||||||
impl<'a> Widget for Layout<'a> {
|
impl<'a> Widget for Layout<'a> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let chunks = layout::Layout::new()
|
let x = plugin::Status::render(self.cx, area);
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
|
||||||
.split(area);
|
|
||||||
|
|
||||||
let x = plugin::Status::layout(self.cx, area);
|
|
||||||
if x.is_err() {
|
if x.is_err() {
|
||||||
info!("{:?}", x);
|
info!("{:?}", x);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,46 @@
|
||||||
function layout(area)
|
Status = {}
|
||||||
local chunks = yazi
|
|
||||||
.Layout()
|
|
||||||
:direction(false)
|
|
||||||
:constraints({ yazi.Constraint.Percentage(50), yazi.Constraint.Percentage(50) })
|
|
||||||
:split(area)
|
|
||||||
|
|
||||||
return yazi.Paragraph.render(
|
function Status.mode()
|
||||||
yazi.Paragraph(mode(), size(), name()):area(chunks[1]),
|
|
||||||
yazi.Paragraph(permissions(), percentage(), position()):area(chunks[2])
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mode()
|
|
||||||
local mode = cx.manager.mode:upper()
|
local mode = cx.manager.mode:upper()
|
||||||
if mode == "UNSET" then
|
if mode == "UNSET" then
|
||||||
mode = "UN-SET"
|
mode = "UN-SET"
|
||||||
end
|
end
|
||||||
|
|
||||||
return yazi.Line(
|
return ui.Line(
|
||||||
yazi.Span(THEME.status.separator.opening):fg(THEME.status.mode_normal.bg),
|
ui.Span(THEME.status.separator.opening):fg(THEME.status.mode_normal.bg),
|
||||||
yazi.Span(" " .. mode .. " "):style(THEME.status.mode_normal)
|
ui.Span(" " .. mode .. " "):style(THEME.status.mode_normal)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function size()
|
function Status.size()
|
||||||
local hovered = cx.manager.current_hovered
|
local hovered = cx.manager.current_hovered
|
||||||
if hovered == nil then
|
if hovered == nil then
|
||||||
return yazi.Span("")
|
return ui.Span("")
|
||||||
end
|
end
|
||||||
|
|
||||||
return yazi.Line(
|
return ui.Line(
|
||||||
yazi.Span(" " .. hovered.length .. " "):fg(THEME.status.mode_normal.bg):bg(THEME.status.fancy.bg),
|
ui.Span(" " .. hovered.length .. " "):fg(THEME.status.mode_normal.bg):bg(THEME.status.fancy.bg),
|
||||||
yazi.Span(THEME.status.separator.closing):fg(THEME.status.fancy.bg)
|
ui.Span(THEME.status.separator.closing):fg(THEME.status.fancy.bg)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function name()
|
function Status.name()
|
||||||
local hovered = cx.manager.current_hovered
|
local hovered = cx.manager.current_hovered
|
||||||
if hovered == nil then
|
if hovered == nil then
|
||||||
return yazi.Span("")
|
return ui.Span("")
|
||||||
end
|
end
|
||||||
|
|
||||||
return yazi.Span(" " .. utils.basename(hovered.url))
|
return ui.Span(" " .. utils.basename(hovered.url))
|
||||||
end
|
end
|
||||||
|
|
||||||
function permissions()
|
function Status.permissions()
|
||||||
local hovered = cx.manager.current_hovered
|
local hovered = cx.manager.current_hovered
|
||||||
if hovered == nil then
|
if hovered == nil then
|
||||||
return yazi.Span("")
|
return ui.Span("")
|
||||||
end
|
end
|
||||||
|
|
||||||
if hovered.permissions == nil then
|
if hovered.permissions == nil then
|
||||||
return yazi.Span("")
|
return ui.Span("")
|
||||||
end
|
end
|
||||||
|
|
||||||
local spans = {}
|
local spans = {}
|
||||||
|
|
@ -65,12 +54,12 @@ function permissions()
|
||||||
elseif c == "x" or c == "s" or c == "S" or c == "t" or c == "T" then
|
elseif c == "x" or c == "s" or c == "S" or c == "t" or c == "T" then
|
||||||
style = THEME.status.permissions_x
|
style = THEME.status.permissions_x
|
||||||
end
|
end
|
||||||
spans[i] = yazi.Span(c):style(style)
|
spans[i] = ui.Span(c):style(style)
|
||||||
end
|
end
|
||||||
return yazi.Line:from(spans)
|
return ui.Line:from(spans)
|
||||||
end
|
end
|
||||||
|
|
||||||
function percentage()
|
function Status.percentage()
|
||||||
local percent = 0
|
local percent = 0
|
||||||
local cursor = cx.manager.current_cursor
|
local cursor = cx.manager.current_cursor
|
||||||
local length = cx.manager.current_length
|
local length = cx.manager.current_length
|
||||||
|
|
@ -84,14 +73,28 @@ function percentage()
|
||||||
percent = string.format(" %3d%% ", percent)
|
percent = string.format(" %3d%% ", percent)
|
||||||
end
|
end
|
||||||
|
|
||||||
return yazi.Line(
|
return ui.Line(
|
||||||
yazi.Span(THEME.status.separator.opening):fg(THEME.status.fancy.bg),
|
ui.Span(THEME.status.separator.opening):fg(THEME.status.fancy.bg),
|
||||||
yazi.Span(percent):fg(THEME.status.mode_normal.bg):bg(THEME.status.fancy.bg)
|
ui.Span(percent):fg(THEME.status.mode_normal.bg):bg(THEME.status.fancy.bg)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function position()
|
function Status.position()
|
||||||
local cursor = cx.manager.current_cursor
|
local cursor = cx.manager.current_cursor
|
||||||
local length = cx.manager.current_length
|
local length = cx.manager.current_length
|
||||||
return yazi.Span(string.format(" %d/%d ", cursor + 1, length))
|
return ui.Span(string.format(" %d/%d ", cursor + 1, length))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Status:render(area)
|
||||||
|
local chunks = ui.Layout()
|
||||||
|
:direction(ui.Direction.HORIZONTAL)
|
||||||
|
:constraints({ ui.Constraint.Percentage(50), ui.Constraint.Percentage(50) })
|
||||||
|
:split(area)
|
||||||
|
|
||||||
|
local left = ui.Line(self.mode(), self.size(), self.name())
|
||||||
|
local right = ui.Line(self.permissions(), self.percentage(), self.position())
|
||||||
|
return ui.Paragraph.render(
|
||||||
|
ui.Paragraph(left):area(chunks[1]),
|
||||||
|
ui.Paragraph(right):align(ui.Alignment.RIGHT):area(chunks[2])
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,15 @@ function Line:from(spans) return self:new(table.unpack(spans)) end
|
||||||
|
|
||||||
function Line:to_string()
|
function Line:to_string()
|
||||||
local s = ""
|
local s = ""
|
||||||
for _, span in ipairs(self.spans) do
|
for _, el in ipairs(self.spans) do
|
||||||
s = s .. span:to_string():gsub("\n", "\\\n") .. "\n"
|
local mt = getmetatable(el)
|
||||||
|
if mt == ui.Line then
|
||||||
|
for _, span in ipairs(el.spans) do
|
||||||
|
s = s .. span:to_string():gsub("\n", "\\\n") .. "\n"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
s = s .. el:to_string():gsub("\n", "\\\n") .. "\n"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return s.sub(s, 1, -2)
|
return s.sub(s, 1, -2)
|
||||||
end
|
end
|
||||||
|
|
@ -24,5 +31,5 @@ setmetatable(Line, {
|
||||||
__tostring = function(self) return self:to_string() end,
|
__tostring = function(self) return self:to_string() end,
|
||||||
})
|
})
|
||||||
|
|
||||||
yazi = yazi or {}
|
ui = ui or {}
|
||||||
yazi.Line = Line
|
ui.Line = Line
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
local Paragraph = {}
|
local Paragraph = {}
|
||||||
|
local Alignment = {
|
||||||
|
LEFT = 0,
|
||||||
|
CENTER = 1,
|
||||||
|
RIGHT = 2,
|
||||||
|
}
|
||||||
|
|
||||||
function Paragraph:new(...)
|
function Paragraph:new(...)
|
||||||
local o = {
|
local o = {
|
||||||
|
alignment = 0,
|
||||||
position = nil,
|
position = nil,
|
||||||
lines = { ... },
|
lines = { ... },
|
||||||
}
|
}
|
||||||
|
|
@ -12,6 +18,11 @@ end
|
||||||
|
|
||||||
function Paragraph:from(lines) return self:new(table.unpack(lines)) end
|
function Paragraph:from(lines) return self:new(table.unpack(lines)) end
|
||||||
|
|
||||||
|
function Paragraph:align(align)
|
||||||
|
self.alignment = align
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
function Paragraph:area(rect)
|
function Paragraph:area(rect)
|
||||||
self.position = rect
|
self.position = rect
|
||||||
return self
|
return self
|
||||||
|
|
@ -25,8 +36,6 @@ function Paragraph:to_string()
|
||||||
return s.sub(s, 1, -2)
|
return s.sub(s, 1, -2)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Paragraph:aaa() return self:to_string() end
|
|
||||||
|
|
||||||
function Paragraph.render(...)
|
function Paragraph.render(...)
|
||||||
local s = "R"
|
local s = "R"
|
||||||
for _, paragraph in ipairs { ... } do
|
for _, paragraph in ipairs { ... } do
|
||||||
|
|
@ -38,6 +47,8 @@ function Paragraph.render(...)
|
||||||
.. paragraph.position.width
|
.. paragraph.position.width
|
||||||
.. ","
|
.. ","
|
||||||
.. paragraph.position.height
|
.. paragraph.position.height
|
||||||
|
.. ","
|
||||||
|
.. paragraph.alignment
|
||||||
.. ";"
|
.. ";"
|
||||||
.. paragraph:to_string():gsub("\0", "\\\0")
|
.. paragraph:to_string():gsub("\0", "\\\0")
|
||||||
.. "\0"
|
.. "\0"
|
||||||
|
|
@ -50,5 +61,6 @@ setmetatable(Paragraph, {
|
||||||
__tostring = function(self) return self:to_string() end,
|
__tostring = function(self) return self:to_string() end,
|
||||||
})
|
})
|
||||||
|
|
||||||
yazi = yazi or {}
|
ui = ui or {}
|
||||||
yazi.Paragraph = Paragraph
|
ui.Paragraph = Paragraph
|
||||||
|
ui.Alignment = Alignment
|
||||||
|
|
|
||||||
|
|
@ -120,5 +120,5 @@ setmetatable(Span, {
|
||||||
__tostring = function(self) return self:to_string() end,
|
__tostring = function(self) return self:to_string() end,
|
||||||
})
|
})
|
||||||
|
|
||||||
yazi = yazi or {}
|
ui = ui or {}
|
||||||
yazi.Span = Span
|
ui.Span = Span
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
local Layout = {}
|
|
||||||
|
|
||||||
function Layout:new(...)
|
|
||||||
local o = {
|
|
||||||
direction = "R",
|
|
||||||
dimension = {},
|
|
||||||
elements = { ... },
|
|
||||||
}
|
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
function Layout:from(elements) return self:new(table.unpack(elements)) end
|
|
||||||
|
|
||||||
function Layout:rows(rows)
|
|
||||||
self.direction = "R"
|
|
||||||
self.dimension = rows
|
|
||||||
return self:to_string()
|
|
||||||
end
|
|
||||||
|
|
||||||
function Layout:cols(cols)
|
|
||||||
self.direction = "C"
|
|
||||||
self.dimension = cols
|
|
||||||
return self:to_string()
|
|
||||||
end
|
|
||||||
|
|
||||||
function Layout:to_string()
|
|
||||||
local s = ""
|
|
||||||
for i, element in ipairs(self.elements) do
|
|
||||||
if i == 1 then
|
|
||||||
s = s .. self.direction
|
|
||||||
end
|
|
||||||
s = s .. self.dimension[i] .. "\0" .. element:to_string():gsub("\0", "\\\0")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
setmetatable(Layout, {
|
|
||||||
__call = function(self, ...) return self:new(...) end,
|
|
||||||
})
|
|
||||||
|
|
||||||
yazi = yazi or {}
|
|
||||||
yazi.Layout = Layout
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use mlua::{AnyUserData, FromLua, Lua, Table, UserData, UserDataMethods, Value};
|
use mlua::{AnyUserData, FromLua, Lua, Table, UserData, UserDataMethods, Value};
|
||||||
use ratatui::layout;
|
use ratatui::layout;
|
||||||
|
|
||||||
use crate::LUA;
|
use crate::{GLOBALS, LUA};
|
||||||
|
|
||||||
// --- Rect
|
// --- Rect
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
@ -39,8 +39,7 @@ pub struct Constraint(layout::Constraint);
|
||||||
|
|
||||||
impl Constraint {
|
impl Constraint {
|
||||||
pub(super) fn install() -> mlua::Result<()> {
|
pub(super) fn install() -> mlua::Result<()> {
|
||||||
let globals = LUA.globals();
|
let ui: Table = GLOBALS.get("ui")?;
|
||||||
let yazi = globals.get::<_, Table>("yazi")?;
|
|
||||||
|
|
||||||
let constraint = LUA.create_table()?;
|
let constraint = LUA.create_table()?;
|
||||||
constraint.set(
|
constraint.set(
|
||||||
|
|
@ -61,7 +60,7 @@ impl Constraint {
|
||||||
constraint
|
constraint
|
||||||
.set("Min", LUA.create_function(|_, n: u16| Ok(Constraint(layout::Constraint::Min(n))))?)?;
|
.set("Min", LUA.create_function(|_, n: u16| Ok(Constraint(layout::Constraint::Min(n))))?)?;
|
||||||
|
|
||||||
yazi.set("Constraint", constraint)
|
ui.set("Constraint", constraint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,10 +89,13 @@ pub struct Layout {
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
pub(super) fn install() -> mlua::Result<()> {
|
pub(super) fn install() -> mlua::Result<()> {
|
||||||
let globals = LUA.globals();
|
let ui: Table = GLOBALS.get("ui")?;
|
||||||
|
ui.set("Layout", LUA.create_function(|_, ()| Ok(Self::default()))?)?;
|
||||||
|
|
||||||
let yazi = globals.get::<_, Table>("yazi")?;
|
let direction = LUA.create_table()?;
|
||||||
yazi.set("Layout", LUA.create_function(|_, ()| Ok(Self::default()))?)
|
direction.set("HORIZONTAL", false)?;
|
||||||
|
direction.set("VERTICAL", true)?;
|
||||||
|
ui.set("Direction", direction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,9 +162,9 @@ impl UserData for Layout {
|
||||||
|
|
||||||
let mut layout = layout::Layout::new()
|
let mut layout = layout::Layout::new()
|
||||||
.direction(if me.direction {
|
.direction(if me.direction {
|
||||||
layout::Direction::Horizontal
|
|
||||||
} else {
|
|
||||||
layout::Direction::Vertical
|
layout::Direction::Vertical
|
||||||
|
} else {
|
||||||
|
layout::Direction::Horizontal
|
||||||
})
|
})
|
||||||
.constraints(me.constraints.as_slice());
|
.constraints(me.constraints.as_slice());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use config::THEME;
|
use config::THEME;
|
||||||
use mlua::{Lua, LuaSerdeExt};
|
use mlua::{Lua, LuaSerdeExt, SerializeOptions, Table};
|
||||||
use shared::RoCell;
|
use shared::RoCell;
|
||||||
|
|
||||||
use crate::layout;
|
use crate::layout;
|
||||||
|
|
||||||
pub(crate) static LUA: RoCell<Lua> = RoCell::new();
|
pub(crate) static LUA: RoCell<Lua> = RoCell::new();
|
||||||
|
pub(crate) static GLOBALS: RoCell<Table> = RoCell::new();
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
fn inner() -> Result<()> {
|
fn inner() -> Result<()> {
|
||||||
|
|
@ -24,12 +25,17 @@ pub fn init() {
|
||||||
lua.load(include_str!("../preset/components/status.lua")).exec()?;
|
lua.load(include_str!("../preset/components/status.lua")).exec()?;
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
lua.globals().set("THEME", lua.to_value(&*THEME)?)?;
|
|
||||||
LUA.init(lua);
|
LUA.init(lua);
|
||||||
|
GLOBALS.init(LUA.globals());
|
||||||
|
|
||||||
|
// Install
|
||||||
layout::Layout::install()?;
|
layout::Layout::install()?;
|
||||||
layout::Constraint::install()?;
|
layout::Constraint::install()?;
|
||||||
|
|
||||||
|
let options =
|
||||||
|
SerializeOptions::new().serialize_none_to_null(false).serialize_unit_to_null(false);
|
||||||
|
GLOBALS.set("THEME", LUA.to_value_with(&*THEME, options)?)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use core::Ctx;
|
use core::Ctx;
|
||||||
|
|
||||||
use mlua::{Function, Result};
|
use mlua::{Result, Table, TableExt};
|
||||||
use ratatui::layout;
|
use ratatui::layout;
|
||||||
|
|
||||||
use crate::{bindings, Rect, LUA};
|
use crate::{bindings, Rect, GLOBALS, LUA};
|
||||||
|
|
||||||
pub struct Status;
|
pub struct Status;
|
||||||
|
|
||||||
|
|
@ -16,16 +16,16 @@ impl Status {
|
||||||
let cx = LUA.create_table()?;
|
let cx = LUA.create_table()?;
|
||||||
cx.set("manager", manager)?;
|
cx.set("manager", manager)?;
|
||||||
cx.set("tasks", tasks)?;
|
cx.set("tasks", tasks)?;
|
||||||
LUA.globals().set("cx", cx)?;
|
GLOBALS.set("cx", cx)?;
|
||||||
|
|
||||||
f()
|
f()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout(cx: &Ctx, area: layout::Rect) -> Result<String> {
|
pub fn render(cx: &Ctx, area: layout::Rect) -> Result<String> {
|
||||||
Self::scope(cx, || {
|
Self::scope(cx, || {
|
||||||
let layout: Function = LUA.globals().get("layout")?;
|
let status: Table = GLOBALS.get("Status")?;
|
||||||
layout.call::<_, String>(Rect::from(area))
|
status.call_method::<_, String>("render", Rect::from(area))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue