Ru-Board.club
← Вернуться в раздел «Тестирование»

» Far Manager - скрипты и плагины

Автор: Angel_Ka
Дата сообщения: 13.05.2016 07:52
VictorVG4

Цитата:
Уверен что у Angel_Ka не стоит StackTracePlus.lua - иначе он смог бы получить более полный стек ошибки.

Благодарю Вас за готовность помочь! Однако, перепроверил и убедился, что данный модуль установлен в той же редакции как и в Ваших сборках. Увы, не помогает. И всё равно спасибо! Ни когда ведь заранее не знаешь, что может сработать.

Добавлено:
Alexyz21

На мой взгляд, было бы удобнее, если это допустимо, чтобы опции

Код: FD should be the same size
и
FD should be the same attributes
Автор: Alexyz21
Дата сообщения: 13.05.2016 10:08
12.9.3 [more]
Код: -- 12.9.3
local F = far.Flags
local ffi = require'ffi'
local C = ffi.C

local PanelMode,Desc1,Indi1 = 999,"Select duplicates","!?"
local guid = "FE9B8874-9651-434C-8182-72329F2371A5"
local uGuid = win.Uuid(guid)
local BS,ts = string.byte("\\"),{nil,true,9999,true,false,2,2,false}
local freport = win.GetEnv("Temp").."\\Report.txt"

ffi.cdef[[
int wcscmp(const wchar_t*, const wchar_t*);
int _wcsicmp(const wchar_t*, const wchar_t*);
int wcsncmp(const wchar_t*, const wchar_t*, size_t);
int _wcsnicmp(const wchar_t*, const wchar_t*, size_t);
]]

local function ToWChar(str)
str=win.Utf8ToUtf16(str)
local res=ffi.new("wchar_t[?]",#str/2+1)
ffi.copy(res,str)
return res
end

local function GetStartAndLenW(name)
local ptr = C.wcsrchr(name,BS)
name = ptr==nil and name or ptr+1
local len = tonumber(C.wcslen(name))
if ts[2] and ts[3]<0 and -ts[3]<len then
local res=ffi.new("wchar_t[?]",len+1)
ffi.copy(res,name+len+ts[3],-ts[3]*2)
ffi.copy(res-ts[3],name,(len+ts[3])*2)
return res,len
else
return name,len
end
end

local function StartAndLenW(name)
local ptr = C.wcsrchr(name,BS)
name = ptr==nil and name or ptr+1
local len = tonumber(C.wcslen(name))
if ts[2] and ts[3]<0 and -ts[3]<len then
return name+len+ts[3],-ts[3],name,len
elseif ts[2] and ts[3]>0 and ts[3]<len then
return name,ts[3],name,len
else
return name,len,name,len
end
end

local Compare = function(p1,p2)
local st1,ln1 = GetStartAndLenW(p1.FileName)
local st2,ln2 = GetStartAndLenW(p2.FileName)
local sz1,sz2,fa1,fa2
if ts[5] and ts[6]~=2 then sz1,sz2 = tonumber(p1.FileSize),tonumber(p2.FileSize) end
if ts[5] and ts[7]~=2 then fa1,fa2 = tonumber(p1.FileAttributes),tonumber(p2.FileAttributes) end
local res = ts[4] and C._wcsicmp(st1,st2) or C.wcscmp(st1,st2)
if res==0 and sz1 then res=sz1-sz2
elseif res==0 and fa1 then res=fa1-fa2
end
return res<0 and -1 or res>0 and 1 or 0
end

local Items = {
--[[01]] {F.DI_DOUBLEBOX, 3,1, 65,8, 0, 0,0, 0, "Select duplicates of FileName. Help: F1"},
--[[02]] {F.DI_CHECKBOX, 5,2, 26,2, 0, 0,0, 0, "Num&ber of symbols"},
--[[03]] {F.DI_EDIT, 27,2, 32,2, 0, 0,0, 0, ""},
--[[04]] {F.DI_CHECKBOX, 5,3, 20,3, 0, 0,0, 0, "Ignore &case"},
--[[05]] {F.DI_CHECKBOX, 38,3, 62,3, 0, 0,0, 0, "Ignore Full &Duplicates"},
--[[06]] {F.DI_CHECKBOX, 5,4, 21,4, 0, 0,0, F.DIF_3STATE, "&Sizes of FD:"},
--[[07]] {F.DI_CHECKBOX, 5,5, 26,5, 0, 0,0, F.DIF_3STATE, "&Attributes of FD:"},
--[[08]] {F.DI_CHECKBOX, 5,7, 15,7, 0, 0,0, 0, "Re&port"},
--[[09]] {F.DI_TEXT, -1,6, 0,0, 0, 0,0, F.DIF_SEPARATOR,""},
--[[10]] {F.DI_BUTTON, 0,7, 0,0, 0, 0,0, F.DIF_DEFAULTBUTTON+F.DIF_CENTERGROUP,"&Ok"},
--[[11]] {F.DI_BUTTON, 0,7, 0,0, 0, 0,0, F.DIF_CENTERGROUP,"Ca&ncel"}
}

local tts={}

local function DlgProc(hDlg,Msg,Param1,Param2)
local function Set1()
hDlg:send(F.DM_ENABLE,6,tts[5] and 1 or 0)
hDlg:send(F.DM_ENABLE,7,tts[5] and 1 or 0)
end
if Msg==F.DN_INITDIALOG then
for i=2,#Items-3 do tts[i]=ts[i] end
Set1()
hDlg:send(F.DM_SETTEXT,3,tts[3])
hDlg:send(F.DM_SETCHECK,2,tts[2] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
hDlg:send(F.DM_SETCHECK,4,tts[4] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
hDlg:send(F.DM_SETCHECK,5,tts[5] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
hDlg:send(F.DM_SETCHECK,6,tts[6]==0 and F.BSTATE_UNCHECKED or tts[6]==1 and F.BSTATE_CHECKED or tts[6]==2 and F.BSTATE_3STATE)
hDlg:send(F.DM_SETTEXT,6,"&Sizes of FD: "..(tts[6]==0 and "<>" or tts[6]==1 and "==" or "--"))
hDlg:send(F.DM_SETCHECK,7,tts[7]==0 and F.BSTATE_UNCHECKED or tts[7]==1 and F.BSTATE_CHECKED or tts[7]==2 and F.BSTATE_3STATE)
hDlg:send(F.DM_SETTEXT,7,"&Attributes of FD: "..(tts[7]==0 and "<>" or tts[7]==1 and "==" or "--"))
hDlg:send(F.DM_SETCHECK,8,tts[8] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
elseif Msg==F.DN_BTNCLICK and (Param1==2 or Param1==4 or Param1==5 or Param1==8) then
tts[Param1] = Param2~=0
if Param1==5 then Set1() end
elseif Msg==F.DN_BTNCLICK and (Param1==6 or Param1==7) then
tts[Param1] = Param2
hDlg:send(F.DM_SETTEXT,Param1,(Param1==6 and "&Sizes of FD: " or "&Attributes of FD: ")..(Param2==0 and "<>" or Param2==1 and "==" or "--"))
elseif Msg==F.DN_EDITCHANGE and Param1==3 then -- Number symbols
tts[3] = tonumber(hDlg:send(F.DM_GETTEXT,3)) or tts[3]
else
return
end
return true
end

Macro {
description="* Select Duplicates FileName in Branch panel"; name="SDFN"; area="Shell";
action=function()
if far.Dialog(uGuid,-1,-1,69,10,nil,Items,nil,DlgProc)==#Items-1 then
local t0=far.FarClock()
for i=2,#Items-3 do ts[i]=tts[i] end
local pBL0,pBL1 = ffi.cast("BOOL*",0),ffi.cast("BOOL*",1)
--local PInfo=panel.GetPanelInfo(nil,1)
local PInfo=ffi.new("struct PanelInfo")
PInfo.StructSize=ffi.sizeof(PInfo)
local pc=ffi.cast("struct PluginStartupInfo*",far.CPluginStartupInfo()).PanelControl
if pc(PANEL_ACTIVE,"FCTL_GETPANELINFO",0,PInfo)==1 then
local pin,pif = tonumber(PInfo.ItemsNumber),tonumber(PInfo.Flags)
if pin>1 then
if bit.band(pif,F.PFLAGS_SELECTEDFIRST)>0 then Keys("ShiftF12") end
if bit.band(pif,F.PFLAGS_NUMERICSORT)>0 then pc(PANEL_ACTIVE,"FCTL_SETNUMERICSORT",0,nil) end
local pfcss=bit.band(pif,F.PFLAGS_CASESENSITIVESORT)==0
if ts[4] and not pfcss or not ts[4] and pfcss then pc(PANEL_ACTIVE,"FCTL_SETCASESENSITIVESORT",ts[4] and 0 or 1,nil) end
if bit.band(pif,F.PFLAGS_REVERSESORTORDER)==0 then pc(PANEL_ACTIVE,"FCTL_SETSORTORDER",1,nil) end
Panel.LoadCustomSortMode(PanelMode,{Description=Desc1;Indicator=Indi1;Compare=Compare})
Panel.SetCustomSortMode(PanelMode,0)
local full,tsel,st0,ln0,st1,ln1,st2,ln2,st3,ln3,sz0,sz1,fa0,fa1 = false,{}
local function Comp(stA,lnA,stB,lnB) return ts[4] and C._wcsicmp(stA,stB) or C.wcscmp(stA,stB) end
local function Proc(i,x)
if ts[5] and x then
if full then
pc(PANEL_ACTIVE,"FCTL_SETSELECTION",i,pBL0)
else
pc(PANEL_ACTIVE,"FCTL_SETSELECTION",i-1,pBL0)
pc(PANEL_ACTIVE,"FCTL_SETSELECTION",i,pBL0)
full=true
end
else
if #tsel==0 or Comp(tsel[#tsel][2],tsel[#tsel][3],st1,ln1)~=0 then
pc(PANEL_ACTIVE,"FCTL_SETSELECTION",i-1,pBL1)
pc(PANEL_ACTIVE,"FCTL_SETSELECTION",i,pBL1)
table.insert(tsel,{2,st1,ln1,win.Utf16ToUtf8(ffi.string(st1,ln1*2))})
else
pc(PANEL_ACTIVE,"FCTL_SETSELECTION",i,pBL1)
tsel[#tsel][1]=tsel[#tsel][1]+1
end
full=false
end
end
local function PGPI(i)
st1,ln1,st3,ln3=StartAndLenW(ToWChar(panel.GetPanelItem(nil,1,i+1).FileName))
if ts[6] then sz1=panel.GetPanelItem(nil,1,i+1).FileSize end
if ts[7] then fa1=panel.GetPanelItem(nil,1,i+1).FileAttributes end
end
PGPI(0)
pc(PANEL_ACTIVE,"FCTL_BEGINSELECTION",0,nil)
for i=1,pin-1 do
st0,ln0,st2,ln2,sz0,fa0 = st1,ln1,st3,ln3,sz1,fa1
PGPI(i)
if ts[4] and (ts[2] and C._wcsnicmp(st1,st0,ts[3])==0 or C._wcsicmp(st1,st0)==0) then Proc(i,C._wcsicmp(st3,st2)==0 and not (ts[6]==0 and sz1==sz0 or ts[6]==1 and sz1~=sz0 or ts[6]==2 and false) and not (ts[7]==0 and fa1==fa0 or ts[7]==1 and fa1~=fa0 or ts[7]==2 and false))
elseif not ts[4] and (ts[2] and C.wcsncmp(st1,st0,ts[3])==0 or C.wcscmp(st1,st0)==0) then Proc(i,C.wcscmp(st3,st2)==0 and not (ts[6]==0 and sz1==sz0 or ts[6]==1 and sz1~=sz0 or ts[6]==2 and false) and not (ts[7]==0 and fa1==fa0 or ts[7]==1 and fa1~=fa0 or ts[7]==2 and false))
end
end
pc(PANEL_ACTIVE,"FCTL_ENDSELECTION",0,nil)
pc(PANEL_ACTIVE,"FCTL_REDRAWPANEL",0,nil)
--Keys("ShiftF12")
if ts[8] then
table.sort(tsel,function(a,b) return a[1]<b[1] end)
local h = io.open(freport,"wb")
io.close(h)
h = io.open(freport,"ab")
h:write("Items: "..#tsel..
"\nExecution time: "..(far.FarClock()-t0)..
" mcs\nNumber of symbols: "..(ts[2] and ts[3] or "all")..
"\nIgnore case: "..tostring(ts[4])..
"\nIgnore Full Duplicates: "..tostring(ts[5])..
(ts[5] and ("\nSizes of FD: "..(ts[6]==0 and "<>" or ts[6]==1 and "==" or "--")) or "")..
(ts[5] and ("\nAttributes of FD: "..(ts[7]==0 and "<>" or ts[7]==1 and "==" or "--")) or "")..
"\n\n")
for i=#tsel,1,-1 do h:write(tsel[i][1].."\t"..tsel[i][4].."\n") table.remove(tsel) end
io.close(h)
far.Message("mcs: "..far.FarClock()-t0,"SDFN")
end
end
end
end
end;
}

Macro {
description = "SDFN - Help"; area = "Dialog"; key = "F1";
condition=function() return Area.Dialog and Dlg.Id==guid end;
action=function()
if Dlg.CurPos<=3 then far.Message("The number of first or last symbols to compare","Help: Number of symbols")
elseif Dlg.CurPos==4 then far.Message("Case of letters in FileName will be ignored","Help: Ignore case")
elseif Dlg.CurPos==5 then far.Message("Full duplicates of FileName will be ignored","Help: Ignore Full Duplicates")
elseif Dlg.CurPos==6 then far.Message("-- ignore, == equal, <> is not equal","Help: Sizes of FD")
elseif Dlg.CurPos==7 then far.Message("-- ignore, == equal, <> is not equal","Help: Attributes of FD")
elseif Dlg.CurPos==8 then far.Message("mcs - total time of execution in mcs\nReport will be saved to:\n"..freport,"Help: Report",nil,"l")
end
end;
}
Автор: Angel_Ka
Дата сообщения: 13.05.2016 15:43
Alexyz21

Цитата:
12.9.3
13.0.3
13.0.3 не падает. Отлично!
И особое спасибо за доходчивые пометки в диалоге: "--", "<>" и "=="!

Пока 13.0.3 ещё не было урывками смотрел 12.9.3, и меня смутили следующие результаты:
Код: [x] Number of symbols 9999
[x] Ignore case [ ] Ignore Full Duplicates

10 файлов (5 пар: одинаковые имена в паре;
есть пары с одинаковыми и есть с разными размерами;
атрибуты в каждой паре одинаковые)

[x] Number of symbols 9999
[x] Ignore case [x] Ignore Full Duplicates
[?] Sizes of FD: --
[ ] Attributes of FD: <>

10 файлов (5 пар: одинаковые имена в паре;
есть пары с одинаковыми и есть с разными размерами;
атрибуты в каждой паре одинаковые)

Файлы одни и те же в обоих случаях.
Автор: Alexyz21
Дата сообщения: 13.05.2016 16:39

Цитата:
меня смутили следующие результаты

И что смущает? - Расхождения с логикой задействованных опций не вижу.
Автор: Angel_Ka
Дата сообщения: 13.05.2016 17:36
Alexyz21

Мне представляется, что в первом случае ищутся только полные дубликаты имён, а во втором — файлы с одинаковыми именами вроде бы должны пропускаться. А в результате помечены одни и те же файлы. Или я не правильно трактую игнорирование? Но тогда, что оно в данном случае означает?. Поясните, пожалуйста, что я тут упускаю.

Код: Тест версий 12.9.3 и 13.0.3 макроса SDFN (автор Alexyz21)

на бранче из 711 954 файлов, сформированном плагином LF Search

[x] Number of symbols 9999
[x] Ignore case [ ] Ignore Full Duplicates

12.9.3 - mcs: 26 289 016 26 090 531 26 388 201

13.0.3 - mcs: 17 833 537 18 013 540 17 901 114


[x] Number of symbols 37
[x] Ignore case [x] Ignore Full Duplicates
[x] Sizes of FD: ==
[x] Attributes of FD: ==

12.9.3 - mcs: 24 472 711 24 478 491 24 455 627

13.0.3 - mcs: 16 930 523 16 881 973 16 812 072


[x] Number of symbols -7
[x] Ignore case [x] Ignore Full Duplicates
[x] Sizes of FD: ==
[x] Attributes of FD: ==

12.9.3 - mcs: 24 459 508 24 518 223 24 405 050

13.0.3 - mcs: 16 856 952 16 997 229 16 838 381

Суммарно версии затратили:

12.9.3 - 225 557 358, 13.0.3 - 155 065 321,

т.е. время работы макроса за счёт нового кода сократилось на 45%!
Автор: Alexyz21
Дата сообщения: 13.05.2016 18:36

Цитата:
Мне представляется, что в первом случае ищутся только полные дубликаты имён

Т.е. хотите,чтобы размеры и атрибуты были задействованы не только при игноре ПД? Тогда правильно реализовывать такое только с отключенным Number of symbols (нужно делать, сейчас размеры и атрибуты работают только при игноре ПД).

На самом деле у текущего алгоритма выделения есть зависимость результата от входной последовательности файлов на панели, но он мизерный плюс-минус файл в цепочке из-за извратной и местами противоречивой логики игноров, приводящей к неопределённости результата в областях перекрытий множеств, определяемых выбранными опциями. Делать точный алгоритм я смысла не вижу - он будет ощутимо медленнее.

Добавлено:

Цитата:
Кажется, это говорит о том, что Вас можно поздравить! Вы ведь этого и добивались? На мой взгляд, так подобный результат просто великолепен!

Вот что значит грамотный маркетинг - главное выбрать медленного партнёра, чтобы на его фоне результат выглядел более впечатляющим!
Нужно учитывать, что добавление новых опций приводит к некоторому замедлению кода, но за всё приходится платить, не так ли? Можете сравнить скорость версий 12.9.x с 12.9.3 при одинаковых настройках.
Автор: Angel_Ka
Дата сообщения: 13.05.2016 19:26
Alexyz21

Цитата:
Изначально вы хотели, чтобы выбирались как полные, так и частичные дубликаты - именно этот выбор и реализуется в первом случае.

Оп-па! Точно! Эт я тупанул в понимании данной комбинации опций. Ведь опция "[ ] Ignore Full Duplicates" означает, что ищутся все дубликаты, в т.ч. и полные. А указатель "9999" заставляет макрос искать совпадения практически со всей длиной имени, т.е. фактически сводит всё именно к полным дубликатам. Отсюда и результат. И именно он то в данном случае и нужен.
Второй пример я, очевидно, тоже трактовал не верно. Но пока чётко сформулировать своё новое понимание его у меня не получается. Изложу как нибудь позже, чтобы сверить его с авторским.

Цитата:
Впоследствии вы захотели не выбирать полные дубликаты – добавил опцию их игнора.
Да, точно. Очень благодарю Вас за это!

Цитата:
Затем завуалированно вы выразили желание иметь возможность считать за полные дубликаты только те, у которых а) совпадают размеры б) совпадают атрибуты с) и эти 2 опции должны быть взаимонезависимы.
Всё верно. Именно "иметь возможность". Очень хорошо!

Цитата:
На самом деле у текущего алгоритма выделения есть зависимость результата от входной последовательности файлов на панели ...
Благодарю Вас за пояснение, принимаю его к сведению.

Цитата:
Делать точный алгоритм я смысла не вижу – он будет ощутимо медленнее.

Вам это однозначно виднее.

Цитата:
Можете сравнить скорость версий 12.9.x с 12.9.3 при одинаковых настройках.

Попозже и это сделаю, прежде всего, для своего более полного понимания.

Ещё и ещё раз БЛАГОДАРЮ Вас за очередной восхитительный макрос! Его возможности значительно превосходят мои первичные ожидания пользы от его создания. Он оказался ещё более полезен. Опять у Вас получился не простой макрос, а почти что плагин, только лучше. Лично я просто в восторге! БОЛЬШОЕ Вам СПАСИБО!

Полагаю, что для выявления и систематизации всех возможных вариаций наборов опций, предусмотренных в макросе, мне необходимо усложнить сформированный ранее микротест и, соответственно, расширить таблицу предполагаемых результатов работы макроса. Этим я займусь в приоритетном порядке.


Автор: Alexyz21
Дата сообщения: 13.05.2016 19:30

Цитата:
Суммарно версии затратили:
12.9.3 - 225 557 358, 13.0.3 - 155 065 321,
т.е. скорость работы макроса за счёт нового кода выросла на 45%!

Да уж, методика расчёта впечатляет, видимо прогнозные показатели роста экономики также расчитываете А у меня 16/24 даёт 33.(3)% прироста, наверное куркулятор неправильный.
Автор: Angel_Ka
Дата сообщения: 13.05.2016 19:44
Сосчитано в Фаре:



Суммы тоже в Фаре считал:

Код: lua:=24459508+24518223+24405050+24472711+24478491+24455627+26289016+26090531+26388201
Автор: Alexyz21
Дата сообщения: 13.05.2016 19:59
Да нетже, всё я правильно сразу сказал - вы перепутали числитель и знаменатель местами, ведь речь шла о том, насколько выросла скорость в %%. Стало быть (1-155/225)*100 = 32%
Автор: Angel_Ka
Дата сообщения: 13.05.2016 20:08
А мне всё равно приятнее знать, что время сократилось на 45%.
Автор: Alexyz21
Дата сообщения: 13.05.2016 20:09
а ну так, конечно Кстати, вы не обратили внимание на то, что время исполнения сильно зависит от того, как отсортированы файлы на панели до запуска скрипта? Возможно тут можно что-то выиграть, побегав с бубном.
Автор: shmuz2
Дата сообщения: 13.05.2016 20:19
На самом деле: время сократилось на 32%, а скорость увеличилась на 45%.
Автор: Angel_Ka
Дата сообщения: 13.05.2016 20:27
Alexyz21

Цитата:
Кстати, вы не обратили внимание на то, что время исполнения сильно зависит от того, как отсортированы файлы на панели до запуска скрипта?

Да, обратил внимание. А по результатам своей работы макрос сортирует файлы всегда одним и тем же манером? Или разными?

Добавлено:
shmuz2

Цитата:
На самом деле: время сократилось на 32%, а скорость увеличилась на 45%.

Даже если цифру брать меньшую, то это очень большая величина при больших количествах файлов. А мне бы хотелось попытаться подобраться к ускорению обработки 20-миллионных баз. И на них полученный выигрыш во времени может оказаться лимитирующим. БОЛЬШОЕ СПАСИБО Вам за предоставление более быстрого кода!
Автор: Alexyz21
Дата сообщения: 13.05.2016 20:43

Цитата:
А мне бы хотелось попытаться подобраться к ускорению обработки 20-миллионных баз.

Да-а... чем дальше в лес, тем толще партизаны. Скоро их на булку можно будет намазывать

Цитата:
А по результатам своей работы макрос сортирует файлы всегда одним и тем же манером?

Мало того, что одним и тем же, так ещё и теми же функциями, которые используются при сравнении имён. Так сделано намеренно, во избежание постреливающих сюрпризов.
Автор: shmuz2
Дата сообщения: 13.05.2016 20:45
Angel_Ka
Неважно, которую цифру брать, меньшую или большую, важно интерпретировать их правильно.
Автор: Angel_Ka
Дата сообщения: 13.05.2016 20:48
Alexyz21

Цитата:
Так сделано намеренно, во избежание постреливающих сюрпризов.

Вот видите, талант то и проявляется, а Вы сомневались.

Цитата:
одним и тем же

А как бы Вы этот способ сортировки охарактеризовали?



Добавлено:
shmuz2

Цитата:
важно интерпретировать их правильно

Вполне с Вами согласен. Но это уже искусство, освоить которое можно только с большой практикой.

Добавлено:
Alexyz21

Вырисовывается у меня сейчас в сознании такая картина. Как Вы это убедительно доказываете своими замечательными макросами, обработать бранч даже очень большого размера можно довольно быстро. Есть также возможность в качестве виртуального слепка большой реальной базы достаточно быстро создать файл-список. А вот создание бранча из реальной ветки папок или из файла-списка это гораздо более длительная процедура. Намного быстрее файл-список загрузить в редактор Фара, чем на Временную панель. Хоть нативную, хоть плагиновую. И на мой вопрос "позволяет ли Фаровский редактор работать со строками так, чтобы получать результаты, достигаемые в бранч-панели макросом от Alexyz21?" уважаемый shmuz2 уверенно ответил: "Позволяет".

Вот я и хотел бы, по возможности, попробовать применить обходной способ обработки относительно больших баз на примере 1,8-миллионой базы. Этот способ видится мне следующей последовательностью операций над файлами. Сначала создать файл-список. Затем загрузить его в редактор Фара. Обработать его в редакторе макросом по образу и подобию Вашего макроса работы в бранч-панели. Результаты обработки поместить в новый производный файл-список. По идее он должен получиться в разы, а то и на порядок меньше исходного. На заключительном этапе сформировать из производного файла-списка бранч, и работать с реальными файлами уже с ним. Умозрительно представляется, что такой способ должен был бы быть достаточно быстрым. И давать возможность гораздо удобнее работать с большими базами, которые ныне приходится обрабатывать медленно, по частям, и с неизбежными издержками. Главный эффект удобства такого способа, поскольку он возможен, заключается в первичной обработке слепка цельной базы, а не её произвольно разделённых частей.

Опробование же этого способа возможно только при Вашем согласии на непосредственное участие в этом. Поскольку центральным пунктом данного способа является создание макроса соответствующей обработки строк в редакторе Фара. А без Вас, я уверен, это практически невозможно, по крайней мере в обозримом времени. И мне бы, разумеется, очень хотелось бы, чтобы Вы взялись за создание такого макроса. Как Вы на это смотрите? Интересна ли для Вас такая задача?
Автор: Alexyz21
Дата сообщения: 14.05.2016 01:39

Цитата:
Интересна ли для Вас такая задача?

Нет. [more]Но подскажу - у Shmuel есть скрипт - DupFighter, который вы можете, если автор не возражает, модифицировать под свои нужды. Для начала отключите FarColorer, и оцените производительность скрипта на тексте в 100.000 имён файлов. Учитывая, что имена у вас не из коротких, думаю весь текст "завесит" МБ на 3 после удаления из имён путей. Но для комфортной работы с объёмами за 1.000.000 потребуется реальный хардкор на FAR API практически ТОТАЛЬНО. И зная ваши хотелки-догонялки, я уже слышу, как в далеке тренькает звоночек хардкорных регэкспов и, возможно, новый счёт за них. А мне пока и предыдущего хватает.[/more]
Автор: Angel_Ka
Дата сообщения: 14.05.2016 01:56
shmuz2

Словил экзотический глюк в плагине LF Search 3.26.3.
Far 3.0.4671 x64 на Win10entRu x64.

До бранчевания панели она выглядит так:


По результатам поиска плагин сформировал бранч на своей временной панели.

Верх панели выглядит пустым, и только в правой колонке видны размеры невидимых выделенных файлов:


А низ панели ещё причудливее:


Добавлено:

Уточнение:
попробовал ещё LF Search 3.26.3 в Far 3.0.4671 x86 на этой же системе Win10entRu x64.

При выставленном
Код: Режим и порядок сортировки
14,0
Автор: Angel_Ka
Дата сообщения: 14.05.2016 08:52
Alexyz21

Цитата:
Цитата:
Интересна ли для Вас такая задача?

Нет.

Жаль, конечно, но что поделаешь? В данном случае интерес не совпал, так тому и быть.
За совет спасибо! Не уверен, что смогу полноценно им воспользоваться из-за колоссального недотягивания до уровня, но посмотрю обязательно.

И ещё раз БЛАГОДАРЮ Вас за прекрасные макросы!
Автор: shmuz2
Дата сообщения: 14.05.2016 09:23
Angel_Ka

Цитата:
Словил экзотический глюк в плагине LF Search 3.26.3.
Far 3.0.4671 x64 на Win10entRu x64.

А что надо сделать, чтобы этот глюк словить?
У меня не повторяется, пробовал с Far 3.0.4671 в трёх вариантах установок: WinXP x86, Win7 x86, Win7 x64.


Добавлено:
В тех случаях, когда глюк у разработчика не воспроизводится, очень многое зависит от репортера, который может исследовать явление на своей системе. Например, с разными версиями Far, с разными версиями плагина. Если репортер заинтересован помочь в поиске неисправности.
Автор: Angel_Ka
Дата сообщения: 14.05.2016 12:08
shmuz2

Цитата:
А что надо сделать, чтобы этот глюк словить?

Вроде бы ничего специально не делаю. У меня в свежей ночной сборке Far 3.0.4671 x64 на Win10entRu x64 глюк в LF Search 3.26.3 воспроизводится запросто, в т.ч. в папке с микротестом. Привожу последовательность [more=скринов]1. Папка до создания бранча.


2. Та же папка с пометкой.


3. Режим и порядок сортировки: 0,0. В режиме 14,0 и 2,0 глюк не воспроизводится.


4. Диалог плагина перед запуском поиска.


5. Верх сформированной бранч-панели.


6. Низ той же панели.


7. Этот же низ с выделенными файлами.


8. Верх этой же панели с выделенными невидимыми файлами.
[/more].
Единственное, что приходит в голову, что могло бы быть особенным в данной ситуации, то это то, что непосредственно до запуска плагина, я тестировал создание бранча на этой же тестовой папке консольной командой, и в созданном ею бранче несколько раз запускал макрос от Alexyz21 последних версий. А больше, вроде бы, ничего и не было, кроме разве что ещё запуска браузера и работы с ним.

Отправлю этот пост, перезагружу комп, и попробую в Фаре сначала запустить плагин. Потом доложусь что получится.
Автор: Alexyz21
Дата сообщения: 14.05.2016 12:34
13.0.4 [more]
Код: -- 13.0.4
local F = far.Flags
local ffi = require'ffi'
local C = ffi.C
local NULL = ffi.cast("void*",0)
local PANEL_ACTIVE = ffi.cast("HANDLE",-1)

local PanelMode,Desc1,Indi1 = 999,"Select duplicates","!?"
local guid = "FE9B8874-9651-434C-8182-72329F2371A5"
local uGuid = win.Uuid(guid)
local BS,ts = string.byte("\\"),{nil,true,9999,true,false,2,2,false}
local freport = win.GetEnv("Temp").."\\Report.txt"

ffi.cdef[[
void free (void*);
int wcscmp(const wchar_t*, const wchar_t*);
int _wcsicmp(const wchar_t*, const wchar_t*);
int wcsncmp(const wchar_t*, const wchar_t*, size_t);
int _wcsnicmp(const wchar_t*, const wchar_t*, size_t);
]]

local function ToWChar(str)
str=win.Utf8ToUtf16(str)
local res=ffi.new("wchar_t[?]",#str/2+1)
ffi.copy(res,str)
return res
end

local function GetStartAndLenW(name)
local ptr = C.wcsrchr(name,BS)
name = ptr==nil and name or ptr+1
local len = tonumber(C.wcslen(name))
if ts[2] and ts[3]<0 and -ts[3]<len then
local res=ffi.new("wchar_t[?]",len+1)
ffi.copy(res,name+len+ts[3],-ts[3]*2)
ffi.copy(res-ts[3],name,(len+ts[3])*2)
return res,len
else
return name,len
end
end

local function StartAndLenW(name)
local ptr = C.wcsrchr(name,BS)
name = ptr==nil and name or ptr+1
local len = tonumber(C.wcslen(name))
if ts[2] and ts[3]<0 and -ts[3]<len then
return name+len+ts[3],-ts[3],name,len
elseif ts[2] and ts[3]>0 and ts[3]<len then
return name,ts[3],name,len
else
return name,len,name,len
end
end

local Compare = function(p1,p2)
local st1,ln1 = GetStartAndLenW(p1.FileName)
local st2,ln2 = GetStartAndLenW(p2.FileName)
local sz1,sz2,fa1,fa2
if (ts[5] or not ts[2]) and ts[6]~=2 then sz1,sz2 = tonumber(p1.FileSize),tonumber(p2.FileSize) end
if (ts[5] or not ts[2]) and ts[7]~=2 then fa1,fa2 = tonumber(p1.FileAttributes),tonumber(p2.FileAttributes) end
local res = ts[4] and C._wcsicmp(st1,st2) or C.wcscmp(st1,st2)
if res==0 and sz1 then res=sz1-sz2
elseif res==0 and fa1 then res=fa1-fa2
end
return res<0 and -1 or res>0 and 1 or 0
end

local Items = {
--[[01]] {F.DI_DOUBLEBOX, 3,1, 65,8, 0, 0,0, 0, "Select duplicates of FileName. Help: F1"},
--[[02]] {F.DI_CHECKBOX, 5,2, 26,2, 0, 0,0, 0, "Num&ber of symbols"},
--[[03]] {F.DI_EDIT, 27,2, 32,2, 0, 0,0, 0, ""},
--[[04]] {F.DI_CHECKBOX, 5,3, 20,3, 0, 0,0, 0, "Ignore &case"},
--[[05]] {F.DI_CHECKBOX, 38,3, 62,3, 0, 0,0, 0, "Ignore Full &Duplicates"},
--[[06]] {F.DI_CHECKBOX, 5,4, 21,4, 0, 0,0, F.DIF_3STATE, "&Sizes of FD:"},
--[[07]] {F.DI_CHECKBOX, 5,5, 26,5, 0, 0,0, F.DIF_3STATE, "&Attributes of FD:"},
--[[08]] {F.DI_CHECKBOX, 5,7, 15,7, 0, 0,0, 0, "Re&port"},
--[[09]] {F.DI_TEXT, -1,6, 0,0, 0, 0,0, F.DIF_SEPARATOR,""},
--[[10]] {F.DI_BUTTON, 0,7, 0,0, 0, 0,0, F.DIF_DEFAULTBUTTON+F.DIF_CENTERGROUP,"&Ok"},
--[[11]] {F.DI_BUTTON, 0,7, 0,0, 0, 0,0, F.DIF_CENTERGROUP,"Ca&ncel"}
}

local tts={}

local function DlgProc(hDlg,Msg,Param1,Param2)
local function Set1()
hDlg:send(F.DM_ENABLE,5,tts[2] and 1 or 0)
hDlg:send(F.DM_ENABLE,6,(tts[5] or not tts[2]) and 1 or 0)
hDlg:send(F.DM_ENABLE,7,(tts[5] or not tts[2]) and 1 or 0)
end
if Msg==F.DN_INITDIALOG then
for i=2,#Items-3 do tts[i]=ts[i] end
Set1()
hDlg:send(F.DM_SETTEXT,3,tts[3])
hDlg:send(F.DM_SETCHECK,2,tts[2] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
hDlg:send(F.DM_SETCHECK,4,tts[4] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
hDlg:send(F.DM_SETCHECK,5,tts[5] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
hDlg:send(F.DM_SETCHECK,6,tts[6]==0 and F.BSTATE_UNCHECKED or tts[6]==1 and F.BSTATE_CHECKED or tts[6]==2 and F.BSTATE_3STATE)
hDlg:send(F.DM_SETTEXT,6,"&Sizes of FD: "..(tts[6]==0 and "<>" or tts[6]==1 and "==" or "--"))
hDlg:send(F.DM_SETCHECK,7,tts[7]==0 and F.BSTATE_UNCHECKED or tts[7]==1 and F.BSTATE_CHECKED or tts[7]==2 and F.BSTATE_3STATE)
hDlg:send(F.DM_SETTEXT,7,"&Attributes of FD: "..(tts[7]==0 and "<>" or tts[7]==1 and "==" or "--"))
hDlg:send(F.DM_SETCHECK,8,tts[8] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
elseif Msg==F.DN_BTNCLICK and (Param1==2 or Param1==4 or Param1==5 or Param1==8) then
tts[Param1] = Param2~=0
if Param1==2 or Param1==5 then Set1() end
elseif Msg==F.DN_BTNCLICK and (Param1==6 or Param1==7) then
tts[Param1] = Param2
hDlg:send(F.DM_SETTEXT,Param1,(Param1==6 and "&Sizes of FD: " or "&Attributes of FD: ")..(Param2==0 and "<>" or Param2==1 and "==" or "--"))
elseif Msg==F.DN_EDITCHANGE and Param1==3 then -- Number symbols
tts[3] = tonumber(hDlg:send(F.DM_GETTEXT,3)) or tts[3]
else
return
end
return true
end

Macro {
description="! Select Duplicates FileName in Branch panel"; name="SDFN"; area="Shell";
action=function()
if far.Dialog(uGuid,-1,-1,69,10,nil,Items,nil,DlgProc)==#Items-1 then
local t0=far.FarClock()
for i=2,#Items-3 do ts[i]=tts[i] end
local pBL0,pBL1 = ffi.cast("BOOL*",0),ffi.cast("BOOL*",1)
--local PInfo=panel.GetPanelInfo(nil,1)
local PInfo=ffi.new("struct PanelInfo")
PInfo.StructSize=ffi.sizeof(PInfo)
local pc=ffi.cast("struct PluginStartupInfo*",far.CPluginStartupInfo()).PanelControl
if pc(PANEL_ACTIVE,"FCTL_GETPANELINFO",0,PInfo)==1 then
local pin,pif = tonumber(PInfo.ItemsNumber),tonumber(PInfo.Flags)
if pin>1 then
if bit.band(pif,F.PFLAGS_SELECTEDFIRST)>0 then Keys("ShiftF12") end
if bit.band(pif,F.PFLAGS_NUMERICSORT)>0 then pc(PANEL_ACTIVE,"FCTL_SETNUMERICSORT",0,NULL) end
local pfcss=bit.band(pif,F.PFLAGS_CASESENSITIVESORT)==0
if ts[4] and not pfcss or not ts[4] and pfcss then pc(PANEL_ACTIVE,"FCTL_SETCASESENSITIVESORT",ts[4] and 0 or 1,NULL) end
if bit.band(pif,F.PFLAGS_REVERSESORTORDER)==0 then pc(PANEL_ACTIVE,"FCTL_SETSORTORDER",1,NULL) end
Panel.LoadCustomSortMode(PanelMode,{Description=Desc1;Indicator=Indi1;Compare=Compare})
Panel.SetCustomSortMode(PanelMode,0)
local tsel,st0,ln0,st1,ln1,st2,ln2,st3,ln3,sz0,sz1,fa0,fa1 = {}
local function Comp(stA,lnA,stB,lnB) return ts[4] and C._wcsicmp(stA,stB) or C.wcscmp(stA,stB) end
local function Proc(i,x)
if ts[5] and x then
elseif ts[2] or not ts[2] and x then
pc(PANEL_ACTIVE,"FCTL_SETSELECTION",i-1,pBL1)
pc(PANEL_ACTIVE,"FCTL_SETSELECTION",i,pBL1)
if #tsel==0 or Comp(tsel[#tsel][2],tsel[#tsel][3],st1,ln1)~=0 then
table.insert(tsel,{2,st1,ln1,win.Utf16ToUtf8(ffi.string(st1,ln1*2))})
else
tsel[#tsel][1]=tsel[#tsel][1]+1
end
end
end
local function PGPI(i)
local ppi = ffi.new("struct FarGetPluginPanelItem")
ppi.StructSize = ffi.sizeof("struct FarGetPluginPanelItem")
ppi.Size = pc(PANEL_ACTIVE,"FCTL_GETPANELITEM",i,NULL)
if ppi.Size~=0 then
local buf = ffi.new("char[?]",ppi.Size)
ppi.Item = ffi.cast("struct PluginPanelItem*",buf)
pc(PANEL_ACTIVE,"FCTL_GETPANELITEM",i,ppi)
st1,ln1,st3,ln3=StartAndLenW(ffi.cast("const unsigned short*",ppi.Item.FileName))
if ts[6] then sz1=tonumber(ppi.Item.FileSize) end
if ts[7] then fa1=tonumber(ppi.Item.FileAttributes) end
--ffi.gc(buf,nil)
end
end
PGPI(0)
pc(PANEL_ACTIVE,"FCTL_BEGINSELECTION",0,NULL)
for i=1,pin-1 do
st0,ln0,st2,ln2,sz0,fa0 = st1,ln1,st3,ln3,sz1,fa1
PGPI(i)
if ts[4] and (ts[2] and C._wcsnicmp(st1,st0,ts[3])==0 or C._wcsicmp(st1,st0)==0) then Proc(i,C._wcsicmp(st3,st2)==0 and not (ts[6]==0 and sz1==sz0 or ts[6]==1 and sz1~=sz0 or ts[6]==2 and false) and not (ts[7]==0 and fa1==fa0 or ts[7]==1 and fa1~=fa0 or ts[7]==2 and false))
elseif not ts[4] and (ts[2] and C.wcsncmp(st1,st0,ts[3])==0 or C.wcscmp(st1,st0)==0) then Proc(i,C.wcscmp(st3,st2)==0 and not (ts[6]==0 and sz1==sz0 or ts[6]==1 and sz1~=sz0 or ts[6]==2 and false) and not (ts[7]==0 and fa1==fa0 or ts[7]==1 and fa1~=fa0 or ts[7]==2 and false))
end
end
pc(PANEL_ACTIVE,"FCTL_ENDSELECTION",0,NULL)
pc(PANEL_ACTIVE,"FCTL_REDRAWPANEL",0,NULL)
--Keys("ShiftF12")
if ts[8] then
table.sort(tsel,function(a,b) return a[1]<b[1] end)
local h = io.open(freport,"wb")
io.close(h)
h = io.open(freport,"ab")
h:write("Items: "..#tsel..
"\nExecution time: "..(far.FarClock()-t0)..
" mcs\nNumber of symbols: "..(ts[2] and ts[3] or "all")..
"\nIgnore case: "..tostring(ts[4])..
"\nIgnore Full Duplicates: "..tostring(ts[5])..
((ts[5] or not ts[2]) and ("\nSizes of FD: "..(ts[6]==0 and "<>" or ts[6]==1 and "==" or "--")) or "")..
((ts[5] or not ts[2]) and ("\nAttributes of FD: "..(ts[7]==0 and "<>" or ts[7]==1 and "==" or "--")) or "")..
"\n\n")
for i=#tsel,1,-1 do h:write(tsel[i][1].."\t"..tsel[i][4].."\n") table.remove(tsel) end
io.close(h)
far.Message("mcs: "..far.FarClock()-t0,"SDFN")
end
end
end
end
end;
}

Macro {
description = "SDFN - Help"; area = "Dialog"; key = "F1";
condition=function() return Area.Dialog and Dlg.Id==guid end;
action=function()
if Dlg.CurPos<=3 then far.Message("The number of first or last symbols to compare","Help: Number of symbols")
elseif Dlg.CurPos==4 then far.Message("Case of letters in FileName will be ignored","Help: Ignore case")
elseif Dlg.CurPos==5 then far.Message("Full duplicates of FileName will be ignored","Help: Ignore Full Duplicates")
elseif Dlg.CurPos==6 then far.Message("-- ignore, == equal, <> is not equal","Help: Sizes of FD")
elseif Dlg.CurPos==7 then far.Message("-- ignore, == equal, <> is not equal","Help: Attributes of FD")
elseif Dlg.CurPos==8 then far.Message("mcs - total time of execution in mcs\nReport will be saved to:\n"..freport,"Help: Report",nil,"l")
end
end;
}
Автор: Angel_Ka
Дата сообщения: 14.05.2016 13:07
shmuz2
Извините, пожалуйста, но причину найти не смог. Первоочерёдность запуска плагина роли не сыграла. А больше ни чего в голову не приходит, в системе всё то же самое, что и раньше.

Добавлено:
Alexyz21

Цитата:
13.0.4
Спасибо! Увидел, начну смотреть.
Автор: shmuz2
Дата сообщения: 14.05.2016 13:17
Angel_Ka
Причину от вас найти и не требуется. Нужно просто подметить обстоятельства, сопутствующие неисправности. Ведь эта неисправность у вас вначале, когда вы выставили в конфигурации 0,0 не проявлялась (иначе вы ещё позавчера бы об этом сообщили). Следовательно, что-то у вас в системе изменилось (например, версия Far, версия плагина), и это только вы можете определить. При желании.
Автор: Angel_Ka
Дата сообщения: 14.05.2016 13:34
shmuz2
Ура! Словил глюк создания бранча консольной командой tmp:<dir /b /s /a-d.
То есть глюк это, конечно, плохо. Но теперь хотя бы понятно, что дело не в плагине.

Добавлено:

Цитата:
что-то у вас в системе изменилось (например, версия Far, версия плагина

Да, ежедневно с утра обновлял Фар до последней ночной сборки и плагин тоже обновлял, практически сразу как он обновлялся.

Добавлено:
[more=Бранч] [/more], созданный консольной командой на той же самой тестовой папке с файлами.
Воспроизводится не каждый раз. Но дважды уже повторился.

Добавлено:
Alexyz21

Похоже, что ввиду обилия вариантов соотношения опций, мне нужно существенно усложнить микротест. Сейчас для последующего анализа отмеченные файлы копирую в отдельную папку по номеру теста — для последующего сравнения файлов в разных папках. На текущий момент получилось следующее.
Код: 1). настройки по умолчанию

[x] Number of symbols 9999
[x] Ignore case [ ] Ignore Full Duplicates

5 пар

2).
[ ] Number of symbols 9999
[x] Ignore case [ ] Ignore Full Duplicates
[?] Sizes of FD: --
[?] Attributes of FD: --

5 пар

3).
[ ] Number of symbols 9999
[ ] Ignore case [ ] Ignore Full Duplicates
[?] Sizes of FD: --
[?] Attributes of FD: --

5 пар

4).
[ ] Number of symbols 9999
[ ] Ignore case [ ] Ignore Full Duplicates
[ ] Sizes of FD: <>
[?] Attributes of FD: --

4 пары

5).
[ ] Number of symbols 9999
[ ] Ignore case [ ] Ignore Full Duplicates
[x] Sizes of FD: ==
[?] Attributes of FD: --

3 пары

6).
[ ] Number of symbols 9999
[ ] Ignore case [ ] Ignore Full Duplicates
[ ] Sizes of FD: <>
[?] Attributes of FD: --

2 пары
Автор: shmuz2
Дата сообщения: 14.05.2016 15:36
Angel_Ka

Цитата:
Дайте, пожалуйста, знать какие версии Фара вы в настоящее время используете для стабильной работы?

Меня интересует информация, позволяющая выйти на неисправность. Не вижу даже попытки сотрудничества с вашей стороны.
Автор: Angel_Ka
Дата сообщения: 14.05.2016 19:12
shmuz2

Цитата:
Меня интересует информация, позволяющая выйти на неисправность.

Задавая свой вопрос:
Цитата:
Angel_Ka

Цитата:
Дайте, пожалуйста, знать какие версии Фара вы в настоящее время используете для стабильной работы?
я предполагал получив ответ сравнить работу в указанной Вами версии с текущей. Потому, что я даже не догадываюсь, какую информацию могу дать, чтобы она вывела на неисправность. Дайте, пожалуйста, мне инструкцию что поэтапно мне надо сделать и я постараюсь сделать это как можно более добросовестно. Можете в этом даже не сомневаться.
Автор: shmuz2
Дата сообщения: 14.05.2016 19:35
Angel_Ka
- Прежде всего точно пошагово описать последовательность действий, начиная от запуска Far и до проявления неисправности.
- Данную последовательность привести здесь.
- Проверить на меньших базах файлов, повторяется ли.
- Если для повторения достаточно стандартной TmpPanel, попробуйте запустить Far только с ней, например из папки Far запустить
Far /pPlugins\TmpPanel и попробовать повторить опыт.

Повторить опыты с предыдущими билдами Far, например, 4665 и 4637 (брать здесь).

Это очень непросто и медленно руководить поиском неисправности дистанционно, но если вы согласны, сделайте вышеуказанное для начала.
Автор: Angel_Ka
Дата сообщения: 14.05.2016 20:17
shmuz2

Цитата:
- Прежде всего точно пошагово описать последовательность действий, начиная от запуска Far и до проявления неисправности.

Запускаю Фар.
В левом пассивном окне автоматически выставляется папка с наиболее часто используемым микротестом W:\farProf\tstSort\ПРОБА2\, в правом активном — папка с панельными макросами W:\Far\Profile\Macros\scripts\she_lua\.
Хоткеем "Tab" перевожу курсор в левое окно.
Хоткеем F11 вызываю меню "Команды плагинов".
Хоткеем "s" вызываю диалог "LuaFAR Search".
Нажимаю "1. Искать" — появляется диалог "LF Search: Поиск", в верхнем поле ввода которого выделены символы "*.*".
Жму "ОК" и буквально за секунду появляется панель "LF Search: временная панель" с ранее показанным видом.
Вот и всё.


Добавлено:
При последующей выгрузке Фара подвисает чёрное окно:

Ни на какие клавиши ни в русской, ни в английской раскладках оно не реагирует. Убиваю процесс Фара программой System Explorer.

Страницы: 123456789101112

Предыдущая тема: кл


Форум Ru-Board.club — поднят 15-09-2016 числа. Цель - сохранить наследие старого Ru-Board, истории становления российского интернета. Сделано для людей.