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

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

Автор: Alexyz21
Дата сообщения: 21.05.2016 07:13
Angel_Ka

Цитата:
как Вы полагаете, его форма уже в окончательном виде или ещё последуют трансформации?

Работа по макросу закончена, поэтому можете модифицировать его как угодно.
Автор: Angel_Ka
Дата сообщения: 21.05.2016 07:33
Alexyz21

Цитата:
Работа по макросу закончена

Ещё раз СПАСИБО Вам БОЛЬШОЕ за очередной прекрасный макрос!

Цитата:
можете модифицировать его как угодно

Мои два высших — экономическое и юридическое очень помогают мне в работе, а вот в составлении макросов не так чтобы. Как Вы смотрите на эту мою хотелку:

Цитата:
по возможности, прошу Вас сделать так, чтобы по результатам работы макроса создавались 3 файла. Первый, Report.txt, — с отчётом о применённых настройках, т.е. с тем, что сейчас в его шапке. Второй, желательно бы с именем типа BranchExt.tmps, — с основным результатом работы макроса по разработанному Вами формату. И третий, просто Branch.tmps, — с результатами работы макроса в сокращённом традиционном стиле.
Автор: Angel_Ka
Дата сообщения: 21.05.2016 22:41
shmuz2

Благодарю Вас за очередную (3.27) версию плагина LuaFAR Search!

Код: [+] Panels: "Panel" menu item.
[+] Panels (configuration): "Preserve contents" option.
[+] A macro for calling the panel.
Автор: shmuz2
Дата сообщения: 21.05.2016 23:28
Angel_Ka
Всё технически можно сделать, но ничего пока не обещаю.
Считывание имён файлов из файла - это на самом деле очень быстрая процедура. Большая часть упомянутых вами 110 секунд тратятся на поиск и исключение дубликатов (которых, как правило, нет - но время тратится), а также на проверку существования файлов на диске (несуществующие на панель не попадают).

Но чтобы вы не огорчались, в качестве "презента" привожу здесь макрос для формирования бранч-файла, который делает это быстрее плагина. (Я надеялся, что вы сами его сделаете или Alexyz21, но вижу, что этого не происходит. Естественно, вы можете этот макрос доработать под свои нужды.

Код: local function search()
local t=Far.UpTime
local scr = far.SaveScreen()
far.Message("Wait...", "", "")
local fp = assert(io.open(APanel.Path.."\\filelist.txt", "w"))
far.RecursiveSearch(APanel.Path, "*",
function(item,fullpath)
if not item.FileAttributes:find("d") then
fp:write(fullpath,"\n")
end
end,
"FRS_RECUR")
fp:close()
far.RestoreScreen(scr)
panel.UpdatePanel(nil,1,true)
panel.RedrawPanel(nil,1)
far.Show(Far.UpTime-t)
end

Macro {
description="Make file list";
area="Shell"; key="CtrlShiftF1";
action=search;
}
Автор: Angel_Ka
Дата сообщения: 22.05.2016 08:34
shmuz2

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

Премного Вам БЛАГОДАРЕН! Этот макрос очень нужен. И после того, как Вы дали подсказку для создания подобного макроса, я попробовал было подбираться к far.RecursiveSearch. В т.ч. попытался исследовать несколько примеров её применения в макросах John Doe, Cavaler и IgorZ, но мой уровень компетенции не позволил мне перейти на практическую стадию. Более того, эти мои попытки показали мне моё гигантское отставание дилетанта от профессионалов. И я оставил это дело на волю случая или м.б. на потом. БОЛЬШОЕ Вам СПАСИБО за ценный подарок!

[more=В скобках сказать]я пришёл к выводу о том, что публикации профессионально составленных макросов упомянутыми и другими авторами очень полезны, наращивают мощь Фара, но, вместе с тем, и несколько отпугивают простого пользователя от их самостоятельного составления. Мне кажется, что разница между макросами, приведёнными в хелпе в качестве примеров, и макросами, публикуемыми последнее время на форуме Фара, все больше и больше возрастает. И посредине образуется некая пустота, некий недостаток в макросах среднего, если можно так выразиться, уровня. На мой взгляд, было бы очень полезно, чтобы наряду с публикацией сложных по своему содержанию и структуре макросов, публиковалось бы и как можно больше относительно простых, решающих ограниченные локальные задачи. Да желательно бы, с пояснениями.

Взять хот бы ту же far.RecursiveSearch. Статью о ней в энциклопедии я отрыл с некоторым трудом, там она именуется как FarRecursiveSearch без точки в имени, и поэтому ни по far.RecursiveSearch, ни по RecursiveSearch не находится. И примеров её применения там нет. Ни простых, ни сложных. А ведь порой стоит пользователю при составлении макроса ошибиться всего на один знак и макрос не взлетает. Отчего, почему? — гадаешь. И вот тут примеры очень помогают. И, кстати, Ваш макрос очень бы в качестве примера подошёл. Ещё раз БОЛЬШОЕ Вам за него СПАСИБО![/more]


Цитата:
вы можете этот макрос доработать под свои нужды

Для простых вещей типа задания другого имени результирующего бранч-списка и другого места его расположения, русского названия самого макроса и т.п. даже и моего уровня подготовки достаточно. А вот, например, сделать так, чтобы в конечный файл-список не попадали папки, мне уже сложно. Ведь простой маской с исключением этого не сделаешь — маска папки не отличима от имени файла без расширения. Т.е. надо делать условие по атрибуту. Что-то типа "if win.GetFileAttr(fullname):find("d") then ...". Но это мне уже не по зубам. А так как результирующий список нужно бы ещё дополнить атрибутами и размерами файлов, то моя надежда теперь, прежде всего, на Alexyz21.
Автор: shmuz2
Дата сообщения: 22.05.2016 09:40
Angel_Ka

1. Макрос отредактирован, чтобы исключить папки.
2. Функция far.RecursiveSearch описана в luafar_manual.chm. В той же статье должна быть ссылка на FarRecursiveSearch, которая лежит в её основе.

Интересно бы узнать о сравнении скоростных характеристик данного макроса с другими методами.
При этом надо учитывать, что первый запуск, как правило, занимает больше времени, чем последующие (для всех методов), так как данные ещё не помещены в кэш операционной системы.
Автор: Angel_Ka
Дата сообщения: 22.05.2016 10:06
shmuz2

Цитата:
1. Макрос отредактирован, чтобы исключить папки.
2. Функция far.RecursiveSearch описана в luafar_manual.chm. В той же статье должна быть ссылка на FarRecursiveSearch, которая лежит в её основе.

БОЛЬШОЕ СПАСИБО Вам за совершенствование макроса и за подсказку по хелпу!

Цитата:
Интересно бы узнать о сравнении скоростных характеристик данного макроса с другими методами.

По сравнению с консольной командой dir /b /s /a-d>bra_.tmps макрос быстрее вдвое на 711-тысячной базе. — но эти данные, по сути, не имеют смысла. Потому, что как Вы помните, оператор перенаправления "<" работает не верно и большое количество файлов выпадают из бранча. Тогда как макрос помещает имена файлов в бранч-список вполне корректно.

Поэтому посмотрел как работают макрос и консольные команды из под альтернативной консоли "Take Command version 19":
соотношение скоростей примерно то же: макрос отрабатывает 711-тысячную базу за 12 сек, dir /b /s /a-d>bra_.tmps — за 25 сек, ffind /A-d /B /S *.*>ffind.tmps тоже за 25 сек. Бранч-листы по количеству файлов макросом и консольными командами создаются идентичные.
Автор: Alexyz21
Дата сообщения: 22.05.2016 10:24
1.1 [more]
Код: -- 1.1
local F = far.Flags
local ffi = require'ffi'
local C = ffi.C
local NULL = ffi.cast("void*",0)
local pHTAB = ffi.cast("void*",win.Utf8ToUtf16("\t"))
local PANEL_ACTIVE = ffi.cast("HANDLE",-1)
local pBL0,pBL1 = ffi.cast("BOOL*",0),ffi.cast("BOOL*",1)
local FSF = ffi.cast("struct PluginStartupInfo*",far.CPluginStartupInfo()).FSF

local guid = "FE9B8874-9651-434C-8182-72329F2371A5"
local uGuid = win.Uuid(guid)
local ZERO,HTAB,BS = ffi.cast("unsigned int",0),ffi.cast("unsigned int",9),string.byte("\\")
local ts = {nil,true,9999,true,false,2,2,true,false}
local Temp = win.GetEnv("Temp")
local FReport = Temp.."\\Report.txt"
local FList = Temp.."\\FList.txt"
local Flags = C.SORT_STRINGSORT
local TAB=win.Utf8ToUtf16("\t")
local tts,FullPathEF,FileSizeEF,FileAttrEF = {}

ffi.cdef[[
unsigned long int wcstoul(const wchar_t*, wchar_t**, int);
unsigned long long int wcstoull(const wchar_t*, wchar_t**, int);
]]

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==NULL 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,size,attr
if FileAttrEF then
attr = tonumber(C.wcstoul(name,pHTAB,0))
--attr = tonumber(FSF.atoi(name))
ptr = C.wcschr(name,HTAB)
name = ptr==NULL and name or ptr+1
end
if FileSizeEF then
size = tonumber(FSF.atoi64(name))
ptr = C.wcschr(name,HTAB)
name = ptr==NULL and name or ptr+1
end
local ptr = C.wcsrchr(name,BS)
name = ptr==NULL 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,size,attr
elseif ts[2] and ts[3]>0 and ts[3]<len then
return name,ts[3],name,len,size,attr
else
return name,len,name,len,size,attr
end
end

local compare = function(p1,p2)
local st1,ln1 = GetStartAndLenW(p1[4])
local st2,ln2 = GetStartAndLenW(p2[4])
local res = -2 + C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,st1,ln1,st2,ln2)
if res==0 and ts[6]~=2 then res=p1[6]-p2[6] end
if res==0 and ts[7]~=2 then res=p1[7]-p2[7] end
return res>0
end

local Items = {
--[[01]] {F.DI_DOUBLEBOX, 3,1, 65,9, 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,6, 35,6, 0, 0,0, 0, "Accuracy (&two-pass method)"},
--[[09]] {F.DI_CHECKBOX, 5,8, 15,8, 0, 0,0, 0, "Re&port"},
--[[10]] {F.DI_TEXT, -1,7, 0,0, 0, 0,0, F.DIF_SEPARATOR,""},
--[[11]] {F.DI_BUTTON, 0,8, 0,0, 0, 0,0, F.DIF_DEFAULTBUTTON+F.DIF_CENTERGROUP,"&Ok"},
--[[12]] {F.DI_BUTTON, 0,8, 0,0, 0, 0,0, F.DIF_CENTERGROUP,"Ca&ncel"}
}

local function DlgProc(hDlg,Msg,Param1,Param2)
if Msg==F.DN_INITDIALOG then
for i=2,#Items-3 do tts[i]=ts[i] end
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_ENABLE,6,FileSizeEF and 1 or 0)
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_ENABLE,7,FileAttrEF and 1 or 0)
hDlg:send(F.DM_SETCHECK,8,tts[8] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
hDlg:send(F.DM_ENABLE,8,FileSizeEF and 1 or 0)
hDlg:send(F.DM_SETCHECK,9,tts[9] 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 or Param1==9) then
tts[Param1] = Param2~=0
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

local UserFunc=function(ppi,FullPath,h)
if not ppi.FileAttributes:find("d") then h:write(ppi.FileSize.."\t"..FullPath.."\n") end
end

Macro {
description="@ Filter Duplicates FileName in Editor"; name="FDFN"; area="Shell Editor";

action=function()
if Area.Shell then
local t0 = far.FarClock()
local h=io.open(FList,"wb") io.close(h) h=io.open(FList,"ab")
far.RecursiveSearch(APanel.Path,"*",UserFunc,F.FRS_RETUPDIR+F.FRS_RECUR+F.FRS_SCANSYMLINK,h)
io.close(h)
far.Message("mcs: "..far.FarClock()-t0,"CLFN")
editor.Editor(FList,nil,0,0,-1,-1,bit64.bor(F.EF_NONMODAL,F.EF_IMMEDIATERETURN,F.EF_OPENMODE_RELOADIFOPEN),1,1,65001)
end
local line=editor.GetStringW(-1,1,0).StringText
if line then
FileSizeEF,FileAttrEF,FullPathEF = regex.matchW(line,[[^(\d+\t)?(\d+\t)?([^\t]+)$]])
ts[6],ts[7] = FileSizeEF and ts[6] or 2,FileAttrEF and ts[7] or 2
end
if far.Dialog(uGuid,-1,-1,69,11,nil,Items,nil,DlgProc)==#Items-1 then
local t0 = far.FarClock()
for i=2,#Items-3 do ts[i]=tts[i] end
Flags = ts[4] and bit.bor(Flags,C.NORM_IGNORECASE) or bit.band(Flags,bit.bnot(C.NORM_IGNORECASE))
local tsel = {}
local ec=ffi.cast("struct PluginStartupInfo*",far.CPluginStartupInfo()).EditorControl
local ei=ffi.new("struct EditorInfo")
ei.StructSize=ffi.sizeof(ei)
if ec(-1,"ECTL_GETINFO",0,ei) then
local LastLine=tonumber(ei.TotalLines)-1
local egs=ffi.new("struct EditorGetString")
egs.StructSize=ffi.sizeof(egs)
local function PGPL(i)
egs.StringNumber=i
if ec(-1,"ECTL_GETSTRING",0,egs) then
local st1,ln1,st3,ln3,sz1,fa1=StartAndLenW(egs.StringText)
table.insert(tsel,{false,st1,ln1,st3,ln3,sz1,fa1,i,egs.StringText})
end
end
for i=0,LastLine do PGPL(i) end
table.sort(tsel,compare)
for i=2,LastLine+1 do
if C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,tsel[i-1][2],tsel[i-1][3],tsel[i][2],tsel[i][3])==2 then
local x = C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,tsel[i-1][4],tsel[i-1][5],tsel[i][4],tsel[i][5])==2
local y = (ts[6]==0 and tsel[i-1][6]~=tsel[i][6] or ts[6]==1 and tsel[i-1][6]==tsel[i][6] or ts[6]==2)
and (ts[7]==0 and tsel[i-1][7]~=tsel[i][7] or ts[7]==1 and tsel[i-1][7]==tsel[i][7] or ts[7]==2)
if not ts[5] and (not ts[2] and x and y or ts[2] and y)
or ts[5] and (ts[6]==0 and tsel[i-1][6]==tsel[i][6] or ts[6]==1 and tsel[i-1][6]~=tsel[i][6])
and (ts[7]==0 and tsel[i-1][7]==tsel[i][7] or ts[7]==1 and tsel[i-1][7]~=tsel[i][7])
then
tsel[i-1][1]=true
tsel[i][1]=true
end
end
end
-- -ts[5] and ts[6]==0
-- +ts[5] and ts[6]==1
-- +not ts[5] and ts[6]==0
-- -not ts[5] and ts[6]==1
if ts[8] then
for i=2,LastLine+1 do
if (((ts[5] and ts[6]==1 or not ts[5] and ts[6]==0) and tsel[i-1][6]==tsel[i][6])
or ((ts[5] and ts[7]==1 or not ts[5] and ts[7]==0) and tsel[i-1][7]==tsel[i][7]))
and C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,tsel[i-1][4],tsel[i-1][5],tsel[i][4],tsel[i][5])==2
then
tsel[i-1][1]=false
tsel[i][1]=false
end
end
end
end
if ts[9] then
local count=0
for i=1,#tsel do if tsel[i][1] then count=count+1 end end
local h=io.open(FReport,"wb") io.close(h) h=io.open(FReport,"ab")
local s = "Items: "..count.."/"..#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])..
"\nSizes of FD: "..(ts[6]==0 and "<>" or ts[6]==1 and "==" or "--")..
"\nAttributes of FD: "..(ts[7]==0 and "<>" or ts[7]==1 and "==" or "--")..
"\nAccuracy (two-pass method): "..tostring(ts[8])..
"\n"..string.rep("-",30).."\n"
h:write(s)
for i=#tsel,1,-1 do if tsel[i][1] then h:write((tsel[i][8]+1).."\t"..win.Utf16ToUtf8(ffi.string(tsel[i][9],tonumber(C.wcslen(tsel[i][9]))*2)).."\n") end table.remove(tsel) end
io.close(h)
editor.Editor(FReport,nil,0,0,-1,-1,bit64.bor(F.EF_NONMODAL,F.EF_IMMEDIATERETURN,F.EF_OPENMODE_RELOADIFOPEN),1,1,65001)
far.Message("mcs: "..far.FarClock()-t0,"FDFN")
end
end
end;
}

Macro {
description = "FDFN - 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("Two-pass method for\n<> (is not equal) options only","Help: Accuracy")
elseif Dlg.CurPos==9 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
Дата сообщения: 22.05.2016 11:11
Alexyz21

БОЛЬШОЕ СПАСИБО! Ниже приведены результаты экспресс-проб.

Микротест 1.1 прошёл отлично.

Показатели замеров в бранч-списке на базе из 2.901.187 файлов:

Код: 1) FullPath 1.0 – 233 сек, 1.1 – 224 сек. (тоже, если поменять строки 51-52).

[x] Number of symbols -7
[ ] Ignore case [ ] Ignore Full Duplicates


2) FileSize-tFullPath 1.0 – 229 сек, 1.1 – 227 сек. (231 если поменять строки 51-52).

[x] Number of symbols -7
[ ] Ignore case [ ] Ignore Full Duplicates
[x] Sizes of FD: ==
[?] Attributes of FD: --
[ ] Accuracy (two-pass method)


3) FileAttributes-tFileSize-tFullPath 1.0 – 228 сек, 1.1 – 262 сек — первый проход;
1.0 – 217 сек, 1.1 – 223 сек — второй проход.

[x] Number of symbols -7
[ ] Ignore case [ ] Ignore Full Duplicates
[x] Sizes of FD: ==
[x] Attributes of FD: ==
[ ] Accuracy (two-pass method)
Автор: Alexyz21
Дата сообщения: 22.05.2016 12:08
Angel_Ka
строки 51-52:
attr = tonumber(C.wcstoul(name,pHTAB,0))
--attr = tonumber(FSF.atoi(name))
заменяем на:
--attr = tonumber(C.wcstoul(name,pHTAB,0))
attr = tonumber(FSF.atoi(name))
попробуйте, посмотрите как изменится результат. Других изменений в коде, отражающихся на времени исполнения, не было.
Автор: Angel_Ka
Дата сообщения: 22.05.2016 12:22
Alexyz21
Возможно, я не вполне корректно замерял: сначала запускал макрос 1.1, а сразу потом 1.0. Сейчас выгрузил Фар, снова вошёл и стал делать замеры наоборот: сначала 1.0, потом 1.1, и результаты поменялись. Это наверное то, о чём выше написал shmuz2:
Цитата:
надо учитывать, что первый запуск, как правило, занимает больше времени, чем последующие (для всех методов), так как данные ещё не помещены в кэш операционной системы
Похоже, что при вторых проходах у обеих версий результаты примерно одинаковые. Перемеряю и поменяю данные в предыдущем посте (upd: сделал). Просто мне это будет вдвое дольше делать. Извините, пожалуйста, за досадный промах.

Добавлено:
И ещё: не нахожу файла FList.txt. Ни в папке C:\Users\Администратор\AppData\Local\Temp\, т.е. там, где Report.txt, ни вообще на диске С:.
Автор: shmuz2
Дата сообщения: 22.05.2016 13:09
Angel_Ka

Цитата:
Просто мне это будет вдвое дольше делать.

Достаточно первый раз запустить любой метод, результаты проигнорировать. После этого можно запускать разные методы по одному разу.
Но не запускать другие действия или программы между тестами, т.к. это может повлиять на кэш.
Автор: Alexyz21
Дата сообщения: 22.05.2016 13:28
Angel_Ka

Цитата:
не нахожу файла FList.txt

потому что он не создавался, а не создавался потому, что макрос запускался в редакторе, а не в той папке, для которой мы хотим создать рекурсивный список файлов. Запуская в редакторе мы обрабатываем открытый в редакторе текст, а запуская в панели, создаём и открываем список файлов в редакторе.
Автор: Angel_Ka
Дата сообщения: 22.05.2016 13:39
shmuz2

Цитата:
Достаточно первый раз запустить любой метод, результаты проигнорировать. После этого можно запускать разные методы по одному разу.
Но не запускать другие действия или программы между тестами, т.к. это может повлиять на кэш.

БЛАГОДАРЮ Вас за совет! Его соблюдение существенно ускорит последующие пробы и обеспечит их б0льшую достоверность.

Alexyz21
В целом, кажется, что по последнему опыту (поменял данные в посте с приведёнными результатами замеров по позиции 3) видно, что время работы версий 1.0 и 1.1 примерно одинаковое (разброс малюсенький, явно не существенный). Подтверждается и указание shmuz2 об особенности первого прохода.

И самое главное это то, что подтверждается актуальность макроса, его дееспособность к работе с большими базами. ОГРОМНОЕ СПАСИБО Вам за его совершенствование!

Цитата:
Запуская в редакторе мы обрабатываем открытый в редакторе текст, а запуская в панели, создаём и открываем список файлов в редакторе.

А я то зациклился на редакторе. БЛАГОДАРЮ Вас за подсказку!

Добавлено:
После перезагрузки системы формирование FList.txt из той же 711-тысячфайловой базы при первом проходе занимает 186 секунд, а при последующих — только 16! Результат кажется невероятным, поэтому перепроверил трижды.
Автор: Angel_Ka
Дата сообщения: 22.05.2016 18:54
shmuz2

БЛАГОДАРЮ Вас за поступательное совершенствование плагина LuaFAR Search!

Посмотрел версию 3.27.1.

Цитата:
[!] Fixes in "Preserve contents" option.
[*] Panels: faster panel creation.

Какие именно изменения внесены в опцию "Сохранять содержимое" (временной панели плагина) я не догадался. Может внутренние, в коде?

А вот насчёт ускорения передачи найденного на временную панель плагина видно явно — теперь это время на той же базе составляет около 90 секунд, вместо прежних 110. Возврат к панели также занимает примерно 90 секунд. На поиск по прежнему уходят 40 секунд.

Сведения приведены как и на прежней версии по третьей попытке. Они практически совпадают со второй. А на первой попытке общее время больше примерно на 100 секунд, в основном за счёт существенного, примерно в 3 раза, увеличения времени поиска.
Автор: shmuz2
Дата сообщения: 22.05.2016 19:31
Angel_Ka

Цитата:
Какие именно изменения внесены в опцию "Сохранять содержимое" (временной панели плагина) я не догадался

Знак [!] означает исправление неисправности, это не изменение, а приведение в норму.
Данная опция у разных пользователей может быть выставлена по-разному, а также пользователь по ходу работы может её менять в обе стороны. Не все эти вариации корректно работали.
Автор: Angel_Ka
Дата сообщения: 22.05.2016 20:38
shmuz2

Цитата:
Не все эти вариации корректно работали.

Благодарю Вас за разъяснение!
Автор: Alexyz21
Дата сообщения: 23.05.2016 09:29
1.2 [more]
Код: -- 1.2
local F = far.Flags
local ffi = require'ffi'
local C = ffi.C
local NULL = ffi.cast("void*",0)
local pHTAB = ffi.cast("void*",win.Utf8ToUtf16("\t"))
local PANEL_ACTIVE = ffi.cast("HANDLE",-1)
local pBL0,pBL1 = ffi.cast("BOOL*",0),ffi.cast("BOOL*",1)
local FSF = ffi.cast("struct PluginStartupInfo*",far.CPluginStartupInfo()).FSF
--local ppi = ffi.cast("struct PluginPanelItem*",ppi)

local guid = "FE9B8874-9651-434C-8182-72329F2371A5"
local uGuid = win.Uuid(guid)
local ZERO,HTAB,BS = ffi.cast("unsigned int",0),ffi.cast("unsigned int",9),string.byte("\\")
local ts = {nil,true,9999,true,false,2,2,true,true}
local Temp = win.GetEnv("Temp")
local FReport = Temp.."\\Report.txt"
local FList = Temp.."\\FList.txt"
local Flags = C.SORT_STRINGSORT
local TAB=win.Utf8ToUtf16("\t")
local tts,FullPathEF,FileSizeEF,FileAttrEF = {}

ffi.cdef[[
unsigned long int wcstoul(const wchar_t*, wchar_t**, int);
unsigned long long int wcstoull(const wchar_t*, wchar_t**, int);
]]

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==NULL 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,size,attr
if FileAttrEF then
attr = tonumber(C.wcstoul(name,pHTAB,0))
--attr = tonumber(FSF.atoi(name))
ptr = C.wcschr(name,HTAB)
name = ptr==NULL and name or ptr+1
end
if FileSizeEF then
size = tonumber(FSF.atoi64(name))
ptr = C.wcschr(name,HTAB)
name = ptr==NULL and name or ptr+1
end
local ptr = C.wcsrchr(name,BS)
name = ptr==NULL 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,size,attr
elseif ts[2] and ts[3]>0 and ts[3]<len then
return name,ts[3],name,len,size,attr
else
return name,len,name,len,size,attr
end
end

local compare = function(p1,p2)
local st1,ln1 = GetStartAndLenW(p1[4])
local st2,ln2 = GetStartAndLenW(p2[4])
local res = -2 + C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,st1,ln1,st2,ln2)
if res==0 and ts[6]~=2 then res=p1[6]-p2[6] end
if res==0 and ts[7]~=2 then res=p1[7]-p2[7] end
return res>0
end

local Items = {
--[[01]] {F.DI_DOUBLEBOX, 3,1, 65,9, 0, 0,0, 0, "Filter 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,6, 35,6, 0, 0,0, 0, "Accuracy (&two-pass method)"},
--[[09]] {F.DI_CHECKBOX, 5,8, 15,8, 0, 0,0, 0, "Re&port"},
--[[10]] {F.DI_TEXT, -1,7, 0,0, 0, 0,0, F.DIF_SEPARATOR,""},
--[[11]] {F.DI_BUTTON, 0,8, 0,0, 0, 0,0, F.DIF_DEFAULTBUTTON+F.DIF_CENTERGROUP,"&Ok"},
--[[12]] {F.DI_BUTTON, 0,8, 0,0, 0, 0,0, F.DIF_CENTERGROUP,"Ca&ncel"}
}

local function DlgProc(hDlg,Msg,Param1,Param2)
if Msg==F.DN_INITDIALOG then
for i=2,#Items-3 do tts[i]=ts[i] end
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_ENABLE,6,FileSizeEF and 1 or 0)
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_ENABLE,7,FileAttrEF and 1 or 0)
hDlg:send(F.DM_SETCHECK,8,tts[8] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
hDlg:send(F.DM_ENABLE,8,FileSizeEF and 1 or 0)
hDlg:send(F.DM_SETCHECK,9,tts[9] 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 or Param1==9) then
tts[Param1] = Param2~=0
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="@ Filter Duplicates FileName in Editor"; name="FDFN"; area="Shell Editor";

action=function()
if Area.Shell then
local t0 = far.FarClock()
local h=io.open(FList,"w+b")
--far.RecursiveSearch(APanel.Path,"*",function(ppi,FullPath,h)
-- if not ppi.FileAttributes:find("d") then h:write(ppi.FileSize.."\t"..FullPath.."\n") end
--end,F.FRS_RETUPDIR+F.FRS_RECUR+F.FRS_SCANSYMLINK,h)
FSF.FarRecursiveSearch(ToWChar(APanel.Path),ToWChar("*"),
function (ppi,FullPath,NULL)
local attr=tonumber(ppi.FileAttributes)
if bit.band(attr,C.FILE_ATTRIBUTE_DIRECTORY)==0 then
h:write(attr.."\t"..tonumber(ppi.FileSize).."\t"..win.Utf16ToUtf8(ffi.string(FullPath,C.wcslen(FullPath)*2)).."\n")
end
return true
end
,F.FRS_RETUPDIR+F.FRS_RECUR+F.FRS_SCANSYMLINK,NULL)
io.close(h)
far.Message("mcs: "..far.FarClock()-t0,"CLFN")
editor.Editor(FList,nil,0,0,-1,-1,bit64.bor(F.EF_NONMODAL,F.EF_IMMEDIATERETURN,F.EF_OPENMODE_RELOADIFOPEN),1,1,65001)
end
local line=editor.GetStringW(-1,1,0).StringText
if line then
FileSizeEF,FileAttrEF,FullPathEF = regex.matchW(line,[[^(\d+\t)?(\d+\t)?([^\t]+)$]])
ts[6],ts[7] = FileSizeEF and ts[6] or 2,FileAttrEF and ts[7] or 2
end
if far.Dialog(uGuid,-1,-1,69,11,nil,Items,nil,DlgProc)==#Items-1 then
local t0 = far.FarClock()
for i=2,#Items-3 do ts[i]=tts[i] end
Flags = ts[4] and bit.bor(Flags,C.NORM_IGNORECASE) or bit.band(Flags,bit.bnot(C.NORM_IGNORECASE))
local tsel = {}
local ec=ffi.cast("struct PluginStartupInfo*",far.CPluginStartupInfo()).EditorControl
local ei=ffi.new("struct EditorInfo")
ei.StructSize=ffi.sizeof(ei)
if ec(-1,"ECTL_GETINFO",0,ei) then
local LastLine=tonumber(ei.TotalLines)-1
local egs=ffi.new("struct EditorGetString")
egs.StructSize=ffi.sizeof(egs)
local function PGPL(i)
egs.StringNumber=i
if ec(-1,"ECTL_GETSTRING",0,egs) then
local st1,ln1,st3,ln3,sz1,fa1=StartAndLenW(egs.StringText)
table.insert(tsel,{false,st1,ln1,st3,ln3,sz1,fa1,i,egs.StringText})
end
end
for i=0,LastLine do PGPL(i) end
table.sort(tsel,compare)
for i=2,LastLine+1 do
if C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,tsel[i-1][2],tsel[i-1][3],tsel[i][2],tsel[i][3])==2 then
local x = C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,tsel[i-1][4],tsel[i-1][5],tsel[i][4],tsel[i][5])==2
local y = (ts[6]==0 and tsel[i-1][6]~=tsel[i][6] or ts[6]==1 and tsel[i-1][6]==tsel[i][6] or ts[6]==2)
and (ts[7]==0 and tsel[i-1][7]~=tsel[i][7] or ts[7]==1 and tsel[i-1][7]==tsel[i][7] or ts[7]==2)
if not ts[5] and (not ts[2] and x and y or ts[2] and y)
or ts[5] and (ts[6]==0 and tsel[i-1][6]==tsel[i][6] or ts[6]==1 and tsel[i-1][6]~=tsel[i][6])
and (ts[7]==0 and tsel[i-1][7]==tsel[i][7] or ts[7]==1 and tsel[i-1][7]~=tsel[i][7])
then
tsel[i-1][1]=true
tsel[i][1]=true
end
end
end
-- -ts[5] and ts[6]==0
-- +ts[5] and ts[6]==1
-- +not ts[5] and ts[6]==0
-- -not ts[5] and ts[6]==1
if ts[8] then
for i=2,LastLine+1 do
if (((ts[5] and ts[6]==1 or not ts[5] and ts[6]==0) and tsel[i-1][6]==tsel[i][6])
or ((ts[5] and ts[7]==1 or not ts[5] and ts[7]==0) and tsel[i-1][7]==tsel[i][7]))
and C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,tsel[i-1][4],tsel[i-1][5],tsel[i][4],tsel[i][5])==2
then
tsel[i-1][1]=false
tsel[i][1]=false
end
end
end
end
if ts[9] then
local count=0
for i=1,#tsel do if tsel[i][1] then count=count+1 end end
local h=io.open(FReport,"w+b")
local s = "Items: "..count.."/"..#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])..
"\nSizes of FD: "..(ts[6]==0 and "<>" or ts[6]==1 and "==" or "--")..
"\nAttributes of FD: "..(ts[7]==0 and "<>" or ts[7]==1 and "==" or "--")..
"\nAccuracy (two-pass method): "..tostring(ts[8])..
"\n"..string.rep("-",30).."\n"
h:write(s)
for i=#tsel,1,-1 do if tsel[i][1] then h:write((tsel[i][8]+1).."\t"..win.Utf16ToUtf8(ffi.string(tsel[i][9],tonumber(C.wcslen(tsel[i][9]))*2)).."\n") end table.remove(tsel) end
io.close(h)
editor.Editor(FReport,nil,0,0,-1,-1,bit64.bor(F.EF_NONMODAL,F.EF_IMMEDIATERETURN,F.EF_OPENMODE_RELOADIFOPEN),1,1,65001)
far.Message("mcs: "..far.FarClock()-t0,"FDFN")
end
end
end;
}

Macro {
description = "FDFN - 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("Two-pass method for\n<> (is not equal) options only","Help: Accuracy")
elseif Dlg.CurPos==9 then far.Message("mcs - total time of execution in mcs\nReport will be saved to:\n"..FReport,"Help: Report",nil,"l")
end
end;
}
Автор: shmuz2
Дата сообщения: 23.05.2016 09:47
В макросе была опечатка в аргументе far.RestoreScreen. Исправлена.
Автор: Angel_Ka
Дата сообщения: 23.05.2016 17:00
Alexyz21

Цитата:
1.2

Создание бранч-списка на базе 711 тыс. файлов.
1-й проход — 127 сек, 2-й и последующие — 18 сек.

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

Замеры в бранч-списке на базе из 2.901.187 файлов при тех же условиях, что и предыдущие, но только по третьему проходу и Фар обновился с 4687 на 4688, в скобках приведены результаты вчерашних замеров:

Код: 1) FullPath (1.0 – 233 сек, 1.1 – 224 сек.) 1.2 – 228 сек.
[x] Number of symbols -7
[ ] Ignore case [ ] Ignore Full Duplicates


2) FileSize-tFullPath (1.0 – 229 сек, 1.1 – 227 сек.) 1.2 – 233 сек.

[x] Number of symbols -7
[ ] Ignore case [ ] Ignore Full Duplicates
[x] Sizes of FD: ==
[?] Attributes of FD: --
[ ] Accuracy (two-pass method)


3) FileAttributes-tFileSize-tFullPath (1.0 – 217 сек, 1.1 – 223 сек.) 1.2 – 236 сек.

[x] Number of symbols -7
[ ] Ignore case [ ] Ignore Full Duplicates
[x] Sizes of FD: ==
[x] Attributes of FD: ==
[ ] Accuracy (two-pass method)
Автор: Alexyz21
Дата сообщения: 23.05.2016 17:39
Angel_Ka
Изменено лишь создание файл-списка, поэтому полученное в тестах различие во времени исполнения определяется исключительно флуктациями производительности вашей системы, либо тестовой базы.

Цитата:
FileSize-tFullPath (1.0 – 229 сек, 1.1 – 227 сек.) 1.2 – 233 сек.

1.1->1.2 время обработки увеличилось из-за присутствия поля с атрибутами, если его удалить, то время обработки изменится не должно.

Цитата:
FileAttributes-tFileSize-tFullPath (1.0 – 217 сек, 1.1 – 223 сек.) 1.2 – 236 сек.

1.1->1.2 время исполнения должно остаться без изменения.
Автор: VictorVG4
Дата сообщения: 23.05.2016 17:54
Alexyz21
Angel_Ka

С командой START разобрались - причина была в пакете msysCORE-1.0.17-1-msys-1.0.17-ext.tar.lzma из которого при установке GCC разворачивается скрипт start:

Цитата:
#!/bin/sh
# Copyright (C) 2002, Earnie Boyd
# mailto:earnie@users.sf.net
# This file is part of Minimal SYStem.
# http://www.mingw.org/msys.shtml
# File: start

cmd //c start "$@"

, а тот и вызывал проблему. Но из-за ./unicode_far/execute.hpp::44 - bool IsExecutable(const string& Filename); проблема гарантировано воспроизводится с любым пустым файлом/скриптом имя которого совпадёт с исполняемым - винда не UNIX и перед запуском не проверяет тип и содержимое файла на то что это программа, симлинк или скрипт и может ли она его запустить? DrKnS предлагает как решение использовать ./addons/SetUp/Executor.*.farconfig в зависимости от вашего шелла, но...
Автор: Angel_Ka
Дата сообщения: 23.05.2016 18:36
Alexyz21

Цитата:
полученное в тестах различие во времени исполнения определяется исключительно флуктациями производительности вашей системы

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

Добавлено:
VictorVG4
Благодарю Вас за полезную и актуальную информацию!
Автор: VictorVG4
Дата сообщения: 23.05.2016 19:05
Angel_Ka

Реальная первопричина явление - ошибка в IsExecutable() WinAPI. Простейший опыт - Win+R -> start cmd и поглядеть что получится.
Автор: Angel_Ka
Дата сообщения: 23.05.2016 22:15
VictorVG4

Цитата:
Win+R -> start cmd и поглядеть что получится.

Спасибо за подсказку! Завтра по-возможности попытаюсь глянуть это на Win7 x32.
Автор: Angel_Ka
Дата сообщения: 24.05.2016 16:18
VictorVG4
Через Win+R ошибка вылезает не только в Win7 x32, но и в Win10entRu x64. Однако при этом запуск start cmd в тех же системах из Фара (4685, 4688 и 4691) проходит без ошибки.
Автор: VictorVG4
Дата сообщения: 24.05.2016 16:31
Angel_Ka

ЧТД - ошибка ОС - она при вызове Win-R в такой ситуации усекает комстороку и не проверяет возврат функции isExecutor().
Автор: Alexyz21
Дата сообщения: 25.05.2016 07:55
1.2.1 Final [more]
Код: -- 1.2.1
local guid = "FE9B8874-9651-434C-8182-72329F2371A5"
local uGuid = win.Uuid(guid)
local Temp = win.GetEnv("Temp")
local FReport = Temp.."\\Report.txt"
local FList = Temp.."\\FList.txt"

local F = far.Flags
local ffi = require'ffi'
local C = ffi.C
local NULL = ffi.cast("void*",0)
local pHTAB = ffi.cast("void*",win.Utf8ToUtf16("\t"))
local PANEL_ACTIVE = ffi.cast("HANDLE",-1)
local pBL0,pBL1 = ffi.cast("BOOL*",0),ffi.cast("BOOL*",1)
local FSF = ffi.cast("struct PluginStartupInfo*",far.CPluginStartupInfo()).FSF
local ZERO,HTAB,BS = ffi.cast("unsigned int",0),ffi.cast("unsigned int",9),string.byte("\\")
local ts = {nil,true,9999,true,false,2,2,true,true}
local Flags = C.SORT_STRINGSORT
local tts,FullPathEF,FileSizeEF,FileAttrEF = {}

ffi.cdef[[
unsigned long int wcstoul(const wchar_t*, wchar_t**, int);
unsigned long long int wcstoull(const wchar_t*, wchar_t**, int);
]]

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==NULL 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,size,attr
if FileAttrEF then
attr = tonumber(C.wcstoul(name,pHTAB,0))
--attr = tonumber(FSF.atoi(name))
ptr = C.wcschr(name,HTAB)
name = ptr==NULL and name or ptr+1
end
if FileSizeEF then
size = tonumber(FSF.atoi64(name))
ptr = C.wcschr(name,HTAB)
name = ptr==NULL and name or ptr+1
end
local ptr = C.wcsrchr(name,BS)
name = ptr==NULL 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,size,attr
elseif ts[2] and ts[3]>0 and ts[3]<len then
return name,ts[3],name,len,size,attr
else
return name,len,name,len,size,attr
end
end

local compare = function(p1,p2)
local st1,ln1 = GetStartAndLenW(p1[4])
local st2,ln2 = GetStartAndLenW(p2[4])
local res = -2 + C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,st1,ln1,st2,ln2)
if res==0 and ts[6]~=2 then res=p1[6]-p2[6] end
if res==0 and ts[7]~=2 then res=p1[7]-p2[7] end
return res>0
end

local Items = {
--[[01]] {F.DI_DOUBLEBOX, 3,1, 65,9, 0, 0,0, 0, "Filter 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,6, 35,6, 0, 0,0, 0, "Accuracy (&two-pass method)"},
--[[09]] {F.DI_CHECKBOX, 5,8, 15,8, 0, 0,0, 0, "Re&port"},
--[[10]] {F.DI_TEXT, -1,7, 0,0, 0, 0,0, F.DIF_SEPARATOR,""},
--[[11]] {F.DI_BUTTON, 0,8, 0,0, 0, 0,0, F.DIF_DEFAULTBUTTON+F.DIF_CENTERGROUP,"&Ok"},
--[[12]] {F.DI_BUTTON, 0,8, 0,0, 0, 0,0, F.DIF_CENTERGROUP,"Ca&ncel"}
}

local function DlgProc(hDlg,Msg,Param1,Param2)
if Msg==F.DN_INITDIALOG then
for i=2,#Items-3 do tts[i]=ts[i] end
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_ENABLE,6,FileSizeEF and 1 or 0)
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_ENABLE,7,FileAttrEF and 1 or 0)
hDlg:send(F.DM_SETCHECK,8,tts[8] and F.BSTATE_CHECKED or F.BSTATE_UNCHECKED)
hDlg:send(F.DM_ENABLE,8,FileSizeEF and 1 or 0)
hDlg:send(F.DM_SETCHECK,9,tts[9] 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 or Param1==9) then
tts[Param1] = Param2~=0
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="@ Filter Duplicates FileName in Editor"; name="FDFN"; area="Shell Editor";

action=function()
local TAB,RET = "\t","\n"
if Area.Shell then
local t0 = far.FarClock()
local h=io.open(FList,"w+b")
FSF.FarRecursiveSearch(ToWChar(APanel.Path),ToWChar("*"),
function (ppi,FullPath,NULL)
local attr=tonumber(ppi.FileAttributes)
if bit.band(attr,C.FILE_ATTRIBUTE_DIRECTORY)==0 then
h:write(tostring(attr)..TAB..tostring(tonumber(ppi.FileSize))..TAB..win.Utf16ToUtf8(ffi.string(FullPath,C.wcslen(FullPath)*2))..RET)
end
return true
end
,F.FRS_RETUPDIR+F.FRS_RECUR+F.FRS_SCANSYMLINK,NULL)
io.close(h)
far.Message("mcs: "..far.FarClock()-t0,"CLFN")
editor.Editor(FList,nil,0,0,-1,-1,bit64.bor(F.EF_NONMODAL,F.EF_IMMEDIATERETURN,F.EF_OPENMODE_RELOADIFOPEN),1,1,65001)
end
local line=editor.GetStringW(-1,1,0).StringText
if line then
FileSizeEF,FileAttrEF,FullPathEF = regex.matchW(line,[[^(\d+\t)?(\d+\t)?([^\t]+)$]])
ts[6],ts[7] = FileSizeEF and ts[6] or 2,FileAttrEF and ts[7] or 2
end
if far.Dialog(uGuid,-1,-1,69,11,nil,Items,nil,DlgProc)==#Items-1 then
local t0 = far.FarClock()
for i=2,#Items-3 do ts[i]=tts[i] end
Flags = ts[4] and bit.bor(Flags,C.NORM_IGNORECASE) or bit.band(Flags,bit.bnot(C.NORM_IGNORECASE))
local tsel = {}
local ec=ffi.cast("struct PluginStartupInfo*",far.CPluginStartupInfo()).EditorControl
local ei=ffi.new("struct EditorInfo")
ei.StructSize=ffi.sizeof(ei)
if ec(-1,"ECTL_GETINFO",0,ei) then
local LastLine=tonumber(ei.TotalLines)
local egs=ffi.new("struct EditorGetString")
egs.StructSize=ffi.sizeof(egs)
local function PGPL(i)
egs.StringNumber=i
if ec(-1,"ECTL_GETSTRING",0,egs) then
local st1,ln1,st3,ln3,sz1,fa1=StartAndLenW(egs.StringText)
table.insert(tsel,{false,st1,ln1,st3,ln3,sz1,fa1,i,egs.StringText})
end
end
for i=0,LastLine-1 do PGPL(i) end
table.sort(tsel,compare)
for i=2,LastLine do
if C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,tsel[i-1][2],tsel[i-1][3],tsel[i][2],tsel[i][3])==2 then
local x = C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,tsel[i-1][4],tsel[i-1][5],tsel[i][4],tsel[i][5])==2
local y = (ts[6]==0 and tsel[i-1][6]~=tsel[i][6] or ts[6]==1 and tsel[i-1][6]==tsel[i][6] or ts[6]==2)
and (ts[7]==0 and tsel[i-1][7]~=tsel[i][7] or ts[7]==1 and tsel[i-1][7]==tsel[i][7] or ts[7]==2)
if not ts[5] and (not ts[2] and x and y or ts[2] and y)
or ts[5] and (ts[6]==0 and tsel[i-1][6]==tsel[i][6] or ts[6]==1 and tsel[i-1][6]~=tsel[i][6])
and (ts[7]==0 and tsel[i-1][7]==tsel[i][7] or ts[7]==1 and tsel[i-1][7]~=tsel[i][7])
then
tsel[i-1][1]=true
tsel[i][1]=true
end
end
end
-- -ts[5] and ts[6]==0
-- +ts[5] and ts[6]==1
-- +not ts[5] and ts[6]==0
-- -not ts[5] and ts[6]==1
if ts[8] then
for i=2,LastLine do
if (((ts[5] and ts[6]==1 or not ts[5] and ts[6]==0) and tsel[i-1][6]==tsel[i][6])
or ((ts[5] and ts[7]==1 or not ts[5] and ts[7]==0) and tsel[i-1][7]==tsel[i][7]))
and C.CompareStringW(C.LOCALE_USER_DEFAULT,Flags,tsel[i-1][4],tsel[i-1][5],tsel[i][4],tsel[i][5])==2
then
tsel[i-1][1]=false
tsel[i][1]=false
end
end
end
end
if ts[9] then
local count=0
for i=1,#tsel do if tsel[i][1] then count=count+1 end end
local h=io.open(FReport,"w+b")
local s = "Items: "..count.."/"..#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])..
"\nSizes of FD: "..(ts[6]==0 and "<>" or ts[6]==1 and "==" or "--")..
"\nAttributes of FD: "..(ts[7]==0 and "<>" or ts[7]==1 and "==" or "--")..
"\nAccuracy (two-pass method): "..tostring(ts[8])..
"\n"..string.rep("-",30).."\n"
h:write(s)
for i=#tsel,1,-1 do if tsel[i][1] then h:write(tostring(tsel[i][8]+1)..TAB..win.Utf16ToUtf8(ffi.string(tsel[i][9],tonumber(C.wcslen(tsel[i][9]))*2))..RET) end table.remove(tsel) end
io.close(h)
editor.Editor(FReport,nil,0,0,-1,-1,bit64.bor(F.EF_NONMODAL,F.EF_IMMEDIATERETURN,F.EF_OPENMODE_RELOADIFOPEN),1,1,65001)
far.Message("mcs: "..far.FarClock()-t0,"FDFN")
end
end
end;
}

Macro {
description = "FDFN - 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("Two-pass method for\n<> (is not equal) options only","Help: Accuracy")
elseif Dlg.CurPos==9 then far.Message("mcs - total time of execution in mcs\nReport will be saved to:\n"..FReport,"Help: Report",nil,"l")
end
end;
}
Автор: shmuz2
Дата сообщения: 25.05.2016 09:00
Alexyz21

Цитата:
LuaMacro ругается, что GUID не С-тип - как его оприходовать?

ffi.copy(edc.Owner, win.Uuid("F4B5E624-16F6-4243-9A3D-763097C72EAA"), ffi.sizeof("GUID"))
Автор: Angel_Ka
Дата сообщения: 25.05.2016 09:24
Alexyz21

Цитата:
1.2.1 Final

БОЛЬШОЕ СПАСИБО!
Бегло посмотрел на реальной базе в 715 тыс.
Формирование первичного бранч-списка — 18 сек.
Формирование отчётного бранч-списка — 53 сек в режиме:
Код: [x] Number of symbols -7
[ ] Ignore case [ ] Ignore Full Duplicates
[x] Sizes of FD: ==
[x] Attributes of FD: ==
[ ] Accuracy (two-pass method)

Страницы: 123456789101112

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


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