Свой список автозамены

Russian main discussion
Post Reply
  • Author
  • Message
Offline
Posts: 14
Joined: Tue Feb 03, 2026 1:20 pm

Свой список автозамены

Post by testuser2 »

Погружаясь в скриптовую автоматизацию AkelPad, я пришел к мысли, что не плохо было бы как-то удобизировать работу с C-структурами, и в итоге написал "фабричную" функцию, создающую обекты-врапперы структур, в которых методы соответствуют полям структур и реализуют запись/чтение этих полей.. Также я написал уже функцию, которая, на основе сишных определений структур в тексте, создает соответствующие "фабрики" объектов - врапперов этих структур. Реализацию и пример использования можно найти [ur=https://github.com/LAcoder2/AkelPadScriptsl]здесь[/url] (файлы MemHelp.js, StructTest,js, RegisterHotkeyExample.js, ExampleEvents.js). Вишенкой на торте хотелось бы дополнить реализацией кастомного списка автозамены, который должен появиться когда вводишь точку в редакторе после имени объекта - враппера структуры.. Обработку событий ввода я уже реализовал в файле ExampleEvents.js. Окно списка автозамены хотелось бы взять стандартное из плагина Coder, которое можно вызвать командой
AkelPad.Call("Coder::AutoComplete::WithList")
Вопрос - реално ли в JS реализовать хук этого окна и заполнение его своими значениями, при этом использя внутренние структуры Сoder-а, чтобы не нарушать его работу? Или лучше делать собственную реализацию этого списка на JScript?

Offline
Posts: 14
Joined: Tue Feb 03, 2026 1:20 pm

Re: Свой список автозамены

Post by testuser2 »

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

Code: Select all

// AutoCompletePopup.js
AkelPad.Include("MemHelp.js")
AkelPad.Include("log.js")

var oSys = AkelPad.SystemFunction()
var hMainWnd = AkelPad.GetMainWnd()
var hInstDLL = AkelPad.GetInstanceDll()
var sClass
var MessageLoopOwnFlag
var gPopup = {hWnd:0, hLB:0, hEdit:0, aItems:[]}

if (!AkelPad.IsInclude())
    ShowAutoCompletePopup(["Яблоко", "Груша", "Апельсин"], 0)

function ShowAutoCompletePopup(aItems, hEdit){
    gPopup.aItems = aItems
    gPopup.hEdit = hEdit || AkelPad.GetEditWnd()
    if (!gPopup.hEdit) return

    sClass = "AkelPad::AutoCompletePopup::" + hInstDLL
    if (hDlg = oSys.Call("User32::FindWindowExW", 0, 0, sClass, 0)){
        oSys.Call("User32::SetForegroundWindow", hDlg)
        return
    }

    var caretPos = GetCaretPos(gPopup.hEdit)
    var nX = caretPos.X
    var nY = caretPos.Y + AkelPad.SendMessage(gPopup.hEdit, 3188, 0, 0)

    AkelPad.WindowRegisterClass(sClass)

    // WS_POPUP | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS (0x90800000)
    // WS_EX_CLIENTEDGE (0x00000200) даёт одинаковую границу по всему контуру, без заголовка
    gPopup.hWnd = oSys.Call("User32::CreateWindowExW",
        0x00000200,
        sClass, "",
        0x90800000,
        nX, nY, 180, 120,
        hMainWnd, 0, hInstDLL, DialogCallback)

    if (!gPopup.hWnd){
        AkelPad.WindowUnregisterClass(sClass)
        return
    }

    oSys.Call("User32::ShowWindow", gPopup.hWnd, 1)
    SetForegroundWindow(gPopup.hWnd)

    if (!AkelPad.ScriptHandle(0, 13)){
        MessageLoopOwnFlag = true
        AkelPad.WindowGetMessage()
        MessageLoopOwnFlag = false
        AkelPad.WindowUnregisterClass(sClass)
    }
}

function DialogCallback(hWnd, uMsg, wParam, lParam){
    switch(uMsg){
        case 1: // WM_CREATE
            gPopup.hLB = oSys.Call("User32::CreateWindowExW",
                0, "LISTBOX", "",
                0x50000000 | 0x00000002 | 0x00000040, // WS_CHILD|WS_VISIBLE|LBS_NOTIFY|LBS_NOINTEGRALHEIGHT
                2, 2, 172, 112,
                hWnd, 1001, hInstDLL, 0)
            var hFont = AkelPad.SendMessage(gPopup.hEdit, 0x0031, 0, 0)
            if (hFont) AkelPad.SendMessage(gPopup.hLB, 0x0030, hFont, true)
            for (var i=0; i<gPopup.aItems.length; i++)
                AkelPad.SendMessage(gPopup.hLB, 0x0180, 0, gPopup.aItems[i])
            AkelPad.SendMessage(gPopup.hLB, 0x018E, 0, 0)
            return 0

        case 273: // WM_COMMAND
            var nID = wParam & 0xFFFF
            var nCode = (wParam >>> 16) & 0xFFFF
            // 2 = LBN_DBLCLK, 1 = LBN_SELCHANGE. Используем двойной клик, чтобы не закрывать при навигации стрелками
            if (nID == 1001 && nCode == 2){
                DoInsertAndClose()
                return 0
            }
            return 0

        case 0x0100: // WM_KEYDOWN
            if (wParam == 13){ // VK_RETURN
                DoInsertAndClose()
                return 0
            }
            if (wParam == 27){ // VK_ESCAPE
                ClosePopup()
                return 0
            }
            return 0

        case 0x0008: // WM_KILLFOCUS
            ClosePopup()
            return 0

        case 16: // WM_CLOSE
            oSys.Call("User32::DestroyWindow", hWnd)
            return 0

        case 2: // WM_DESTROY
            gPopup.hWnd = 0
            gPopup.hLB = 0
            if (MessageLoopOwnFlag) oSys.Call("User32::PostQuitMessage", 0)
            return 0
    }
    return 0
}

function DoInsertAndClose(){
    if (!gPopup.hLB) return
    var nSel = AkelPad.SendMessage(gPopup.hLB, 0x0188, 0, 0) // LB_GETCURSEL
    if (nSel == -1) return
    var nLen = AkelPad.SendMessage(gPopup.hLB, 0x018D, nSel, 0) // LB_GETTEXTLEN
    if (nLen <= 0 || nLen > 32768) return
    var sBuf = makeStrBuff(nLen + 1)
    var lpBuf = AkelPad.MemStrPtr(sBuf)
    AkelPad.SendMessage(gPopup.hLB, 0x0189, nSel, lpBuf) // LB_GETTEXT
    var sText = sBuf.replace(/\0+$/, "")
    oSys.Call("User32::SetFocus", gPopup.hEdit)
    AkelPad.ReplaceSel(sText)
    ClosePopup()
}

function ClosePopup(){
    if (gPopup.hWnd && oSys.Call("User32::IsWindow", gPopup.hWnd))
        oSys.Call("User32::PostMessageW", gPopup.hWnd, 16, 0, 0)
}

// --- Вспомогательные функции (без изменений, как в вашем шаблоне) ---
function GetCaretPos(hEdit){
    var sPointBuf = makeStrBuff(4)
    var lpPoint = sPointBuf.StrPtr()
    return (GetCaretPos = function(hEdit){
        if (!hEdit) hEdit = AkelPad.GetEditWnd()
        AkelPad.SendMessage(hEdit, 0x00D6, lpPoint, AkelPad.GetSelStart())
        oSys.Call("User32::ClientToScreen", hEdit, lpPoint)
        return {
            X: AkelPad.MemRead(lpPoint, 3),
            Y: AkelPad.MemRead(_PtrAdd(lpPoint, 4), 3)
        }
    })(hEdit)
}

function SetForegroundWindow(hWnd){
    if (oSys.Call("User32::IsIconic", hWnd)) oSys.Call("User32::ShowWindow", hWnd, 9)
    oSys.Call("User32::SetForegroundWindow", hWnd)
}
Post Reply