; checks for procedure call on current line of text and then shows a hint window with
; procedure definition line (if available)

cCallStdCall text 'stdcall'
cCallCCall   text 'ccall'
cCallInvoke  text 'invoke'
cCallCInvoke text 'cinvoke'
cProcName    text 'proc '

iglobal
  cPAFontName du 'Trebuchet MS', 0
endg


uglobal
  hPAHint dd ?
  hPAFontNormal dd ?
  hPAFontBold   dd ?
endg

initialize InitPAHint
begin
        invoke  CreateFontW, -12, 0, 0, 0, FW_NORMAL,                 \
                            0, 0, 0, 0,                              \
                            OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,           \
                            FF_DONTCARE, cPAFontName
        mov     [hPAFontNormal], eax

        invoke  CreateFontW, -12, 0, 0, 0, FW_BOLD,                                                \
                            0, 0, 0, 0,                         \
                            OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,           \
                            FF_DONTCARE, cPAFontName
        mov     [hPAFontBold], eax
        return
endp


finalize FreePAHint
begin
        invoke  DeleteObject, [hPAFontNormal]
        invoke  DeleteObject, [hPAFontBold]
        return
endp




proc CreatePAHint, .parent
begin
        cmp     [hPAHint], 0
        jne     .exit

        invoke  CreateWindowExW, 0, cStaticClassName, 0,                   \
                WS_POPUP or WS_BORDER or SS_OWNERDRAW or SS_LEFT,         \
                10, 10, 350, 300, [.parent], 0, [hInstance], 0
        mov     [hPAHint], eax
        stdcall SubclassWindow, eax, procPAHint

.exit:
        invoke  SetPropW, [hPAHint], [propOwnerAsmEdit], [.parent]
        call    UpdatePAHint
        return
endp



; Returns pointer to the string with current line from the editor.
; If the current line is continuation of some of the previous lines, they are included as well to the line.
;
; returns:
; eax pointer to the line buffer.
; ecx contains offset in the string of the cursor.
proc GetCurrentLine, .asmedit
.aepos AEPOS

.ptrbuffer   dd ?
.buffersize  dd ?
.bufferlimit dd ?

.caret  dd ?

begin
        push    ebx esi edi

        mov     [.buffersize], $800
        mov     [.bufferlimit], $800 - aeLineDataLen

        stdcall GetMem, [.buffersize]
        mov     edi, eax
        mov     [.ptrbuffer], eax

        lea     eax, [.aepos]
        invoke  SendMessageW, [.asmedit], AEM_GETPOS, eax, 0

        mov     ebx, [.aepos.caretLine]

.begloop:

        dec     ebx
        jz      .lineloop

        call    .IsLineMerged
        jc      .begloop

.lineloop:

        inc     ebx

        cmp     ebx, [.aepos.caretLine]
        ja      .endprocessing
        jne     .caretok

        mov     eax, edi
        sub     eax, [.ptrbuffer]
        shr     eax, 1
        mov     [.caret], eax

.caretok:

        mov     eax, edi
        sub     eax, [.ptrbuffer]
        cmp     eax, [.bufferlimit]
        jb      .bufferok

        sub     edi, [.ptrbuffer]
        shl     [.buffersize], 1

        stdcall ResizeMem, [.ptrbuffer], [.buffersize]
        mov     [.ptrbuffer], eax
        add     edi, eax

        push    [.buffersize]
        pop     [.bufferlimit]
        sub     [.bufferlimit], aeLineDataLen

.bufferok:

        invoke  SendMessageW, [.asmedit], AEM_GETLINEDATA, ebx, edi

        xor     ecx, ecx
        dec     ecx
        xor     esi, esi

.end_search_loop:

        inc     ecx
        cmp     ecx, aeLineCharLen
        je      .endline1

        cmp     word [edi + aeCharSize * ecx], ';'
        je      .endline

        cmp     word [edi + aeCharSize * ecx], '\'
        je      .endline

        cmp     word [edi + aeCharSize * ecx], $20
        je      .end_search_loop

        lea     esi, [ecx + 1]
        jmp     .end_search_loop

.endline1:
        mov     ecx, esi

.endline:
        lea     edi, [edi + aeCharSize * ecx + aeCharSize]
        mov     word [edi - aeCharSize], $20
        jmp     .lineloop


.endprocessing:

        mov     dword [edi - aeCharSize], 0

        stdcall WideCharToUtf8, [.ptrbuffer]
        stdcall FreeMem, [.ptrbuffer]

        mov     ecx, [.caret]
        add     ecx, [.aepos.caretPosition]
        dec     ecx

        pop     edi esi ebx
        return

.IsLineMerged:

        invoke  SendMessageW, [.asmedit], AEM_GETLINEPTR, ebx, 0
        xor     ecx, ecx

.mergloop:

        cmp     word [eax + AELINE.text + aeCharSize * ecx], ';'
        je      .notmerged

        cmp     word [eax + AELINE.text + aeCharSize * ecx], '\'        ; must be checked for the next characters.
        je      .merged

        inc     ecx
        cmp     ecx, aeLineCharLen
        jne     .mergloop

.notmerged:
        clc
        retn

.merged:
        stc
        retn
endp




proc UpdatePAHint
  .aepos   AEPOS
  .caret   AECARETXY
  .hBuffer dd ?
  .arg     dd ?
  .save    dd ?
  .edit    dd ?
  .begarg  dd ?
begin
        cmp     [hPAHint], 0
        je      .exit

        invoke  GetPropW, [hPAHint], [propOwnerAsmEdit]
        mov     [.edit], eax

        stdcall StrDup, cProcName
        mov     [.arg], eax

        lea     eax, [.aepos]
        invoke  SendMessageW, [.edit], AEM_GETPOS, eax, 0

        stdcall GetCurrentLine, [.edit]
        mov     [.hBuffer], eax
        mov     esi, eax
        mov     [.aepos.caretPosition], ecx

        mov     ebx, cCallCCall.length
        stdcall StrPos, esi, cCallCCall
        test    eax, eax
        jnz     .found

        mov     ebx, cCallInvoke.length
        stdcall StrPos, esi, cCallInvoke
        test    eax, eax
        jnz     .found

        mov     ebx, cCallCInvoke.length
        stdcall StrPos, esi, cCallCInvoke
        test    eax, eax
        jnz     .found

        mov     ebx, cCallStdCall.length
        stdcall StrPos, esi, cCallStdCall
        test    eax, eax
        jz      .notfound

; search the name of the procedure
.found:
        lea     esi, [eax + ebx]

.loop:
        inc     esi
        cmp     byte [esi], 0
        je      .notfound

        cmp     byte [esi], ' '
        je      .loop

        mov     ebx, esi        ; begin of the name.

.endloop:
        inc     esi
        mov     al, [esi]
        cmp     al, 0
        je      .endfound

        cmp     al, ','
        je      .endfound

        cmp     al, ' '
        jne     .endloop

.endfound:
        mov     [.begarg], esi
        pushd   [esi]
        popd    [.save]
        mov     byte [esi], 0

        mov     edi, [ptrPreprocessed]
        test    edi, edi
        jz      .notfound

        stdcall SearchTreeExact, ebx, ptrLabels
        test    eax, eax
        jz      .notfound

        push    eax
        stdcall StrCat, [.arg], ebx

        mov     eax, [.save]
        mov     [esi], eax
        pop     esi             ; esi - pointer to TLabel with the procedure label.

        mov     esi, [esi+TLabel.Children]
        test    esi, esi
        jz      .endsearch

        mov     edi, [esi+TArray.count]
        lea     esi, [esi+TArray.array]

        cmp     edi, 0
        jle     .endsearch

.loopchild:
        cmp     [esi+TLabel.ValueLo], 4
        jl      .nextchild

        cmp     [esi+TLabel.SIBEx], $00010045
        je      @f
        cmp     [esi+TLabel.SIBEx], $00010025
        jne     .nextchild

@@:
        mov     eax, [esi+TLabel.iName]
        add     eax, [ptrNames]
        stdcall StrCharCat, [.arg], ', .'
        stdcall StrCat, [.arg], eax

.nextchild:
        add     esi, sizeof.TLabel
        dec     edi
        jnz     .loopchild

.endsearch:

; what argument is now?
        mov     esi, [.begarg]

        stdcall StrOffsUtf8, [.hBuffer], [.aepos.caretPosition]
        mov     ebx, eax

        xor     ecx, ecx
        inc     ecx

; here allows use call macros with space (like the standard FASM macros) or with comma
; (like in FreshLib and the old FASM macros) separated arguments
.skip_first:
        cmp     esi, ebx
        jae     .endcount

        lodsb
        cmp     al, ' '
        je      .skip_first
        cmp     al, ','
        jne     .commaloop

        dec     ecx     ; comma separated arguments has been used.
        dec     esi

.commaloop:
        cmp     esi, ebx
        jae     .endcount
        lodsb

        cmp     al, "'"
        je      .skip_quoted
        cmp     al, '"'
        je      .skip_quoted

        cmp     al, ','
        jne     .commaloop
        inc     ecx
        jmp     .commaloop

.skip_quoted:
        mov     ah, al
.quoteloop:
        lodsb
        cmp     al, ah
        je      .commaloop
        cmp     esi, ebx
        jb      .quoteloop

.endcount:
        invoke  SetWindowLongW, [hPAHint], GWL_USERDATA, ecx

locals
  .info   MONITORINFO
  .height dd ?
endl
; then set the possition
  ; first, compute the size of the window.

        stdcall SetControlTextUtf8, [hPAHint], [.arg]

        invoke  GetDC, [hPAHint]
        push    eax
        invoke  CreateCompatibleDC, eax
        push    eax
        stdcall PaintPAHint, [hPAHint], eax
        mov     [.height], eax
        invoke  DeleteDC ; from the stack
        invoke  ReleaseDC, [hPAHint] ; from the stack

  ; then compute the place

        lea     ebx, [.caret]
        invoke  SendMessageW, [.edit], AEM_GETCARETXY, ebx, 0
        invoke  ClientToScreen, [.edit], ebx
        add     ebx, 8
        invoke  ClientToScreen, [.edit], ebx
        sub     ebx, 8

        invoke  MonitorFromWindow, [.edit], 0

        mov     [.info.cbSize], sizeof.MONITORINFO
        lea     ecx, [.info]
        invoke  GetMonitorInfoW, eax, ecx

        mov     eax, 350
        mov     edx, [.height]

        mov     ecx, [ebx+AECARETXY.x0]
        add     eax, ecx
        sub     [.info.rcWork.right], eax
        jns     @f
        add     ecx, [.info.rcWork.right]
@@:
        mov     [.info.rcWork.right], ecx

        mov     ecx, [ebx+AECARETXY.y1]
        sub     [.info.rcWork.bottom], edx
        cmp     ecx, [.info.rcWork.bottom]
        jbe     @f
        mov     ecx, [ebx+AECARETXY.y0]
        sub     ecx, edx
@@:
        mov     [.info.rcWork.top], ecx

        invoke  SetWindowPos, [hPAHint], 0, [.info.rcWork.right], ecx, 350, [.height], SWP_NOZORDER or SWP_NOACTIVATE or SWP_SHOWWINDOW
        invoke  InvalidateRect, [hPAHint], 0, TRUE

.finish:
        stdcall StrDel, [.arg]
        stdcall StrDel, [.hBuffer]

.exit:
        return

.notfound:
        invoke  PostMessageW, [hPAHint], WM_CLOSE, 0, 0
        jmp     .finish
endp








proc procPAHint, .hwnd, .wmsg, .wparam, .lparam
begin

        mov     ebx, [.wmsg]
        dispatch ebx

.ondefault:
        stc
        return

;___________________________________________________________________________
;
oncase WM_ERASEBKGND
cPABackground = $e0ffff
locals
  .bkrect RECT
endl
        lea     ebx, [.bkrect]
        invoke  GetClientRect, [.hwnd], ebx
        invoke  CreateSolidBrush, cPABackground
        push    eax
        invoke  FillRect, [.wparam], ebx, eax
        invoke  DeleteObject ; from the stack
        xor     eax, eax
        dec     eax
        clc
        return

;___________________________________________________________________________
;
oncase WM_PAINT
locals
  .update RECT
  .ps     PAINTSTRUCT
endl

cPAColorGray  = $000000
cPAColorLight = $ff0000

        lea     eax, [.update]
        invoke  GetUpdateRect, [.hwnd], eax, TRUE
        test    eax, eax
        jz      .finishpaint

        lea     esi, [.ps]
        invoke  BeginPaint, [.hwnd], esi
        test    eax, eax
        jz      .endpaint

        stdcall PaintPAHint, [.hwnd], [esi+PAINTSTRUCT.hdc]

.endpaint:
        invoke  EndPaint, [.hwnd], esi

.finishpaint:
        xor     eax, eax
        clc
        return


;___________________________________________________________________________
;
oncase WM_CLOSE
        invoke  DestroyWindow, [.hwnd]
        mov     [hPAHint], 0
        clc
        return


        enddispatch
endp




; paints the hint window and returns the size needed.
; eax: height of the window needed.
proc PaintPAHint, .hwnd, .hdc
.str dd ?
.deffont dd ?
.point POINT
.metrics TEXTMETRIC
begin
        push    ebx edi

        stdcall GetControlTextUtf8, [.hwnd]
        mov     [.str], eax

        stdcall StrPtr, eax
        mov     edi, eax

        stdcall StrLen, edi
        mov     ebx, eax

        invoke  GetWindowLongW, [.hwnd], GWL_USERDATA
        test    eax, eax
        je      .textready

        xor     ebx, ebx
        dec     ebx

.commaloop:
        inc     ebx
        cmp     byte [edi+ebx], 0
        je      .textready

        cmp     byte [edi+ebx], ','
        jne     .commaloop
        dec     eax
        jnz     .commaloop

.here:
        inc     ebx

.textready:
        invoke  SetTextAlign, [.hdc], TA_LEFT or TA_TOP or TA_UPDATECP
        invoke  SetTextColor, [.hdc], cPAColorGray
        invoke  SetBkMode, [.hdc], TRANSPARENT
        invoke  SelectObject, [.hdc], [hPAFontNormal]
        mov     [.deffont], eax

        lea     eax, [.metrics]
        invoke  GetTextMetricsW, [.hdc], eax

        mov     eax, 4
        sub     eax, [.metrics.tmInternalLeading]

        invoke  MoveToEx, [.hdc], 2, eax, 0
        stdcall TextOutWithWrap, [.hdc], 350, edi, ebx

        cmp     byte [edi+ebx], 0
        je      .enddraw

        lea     edi, [edi+ebx]
        xor     ebx, ebx
        dec     ebx

.comma2:
        inc     ebx
        cmp     byte [edi+ebx], 0
        je      .drawbold
        cmp     byte [edi+ebx], ','
        jne     .comma2

.drawbold:
        invoke  SelectObject, [.hdc], [hPAFontBold]
        invoke  SetTextColor, [.hdc], cPAColorLight

        stdcall TextOutWithWrap, [.hdc], 350, edi, ebx

        cmp     byte [edi+ebx], 0
        je      .enddraw

        lea     edi, [edi+ebx]
        invoke  SelectObject, [.hdc], [hPAFontNormal]
        invoke  SetTextColor, [.hdc], cPAColorGray
        stdcall StrLen, edi

        stdcall TextOutWithWrap, [.hdc], 350, edi, eax

.enddraw:
        invoke  SelectObject, [.hdc], [.deffont]
        stdcall StrDel, [.str]

        lea     eax, [.point]
        invoke  MoveToEx, [.hdc], 0, 0, eax

        mov     eax, [.metrics.tmHeight]
        add     eax, [.point.y]
        add     eax, 2
        pop     edi ebx
        return
endp




proc TextOutWithWrap, .hdc, .width, .ptrstring, .count
.size   SIZE
.point  POINT
.buffer rw 1024
begin
        push    ebx esi

        mov     esi, [.ptrstring]
.wordloop:
        xor     ebx, ebx

.loopstring:
        cmp     ebx, [.count]
        jae     .word2

        cmp     byte [esi+ebx], ' '
        je      .word
        cmp     byte [esi+ebx], ','
        je      .word

        inc     ebx
        jmp     .loopstring

.word:
        inc     ebx
.word2:

        lea     eax, [.buffer]
        invoke  MultiByteToWideChar, CP_UTF8, 0, esi, ebx, eax, 1024

        lea     edx, [.size]
        lea     ecx, [.buffer]
        invoke  GetTextExtentPoint32W, [.hdc], ecx, eax, edx
        lea     eax, [.point]
        invoke  MoveToEx, [.hdc], 0, 0, eax
        invoke  MoveToEx, [.hdc], [.point.x], [.point.y], 0

        mov     eax, [.size.cx]
        add     eax, [.point.x]
        cmp     eax, [.width]
        jbe     .outputit

        cmp     [.point.x], 2   ; left most point.
        je      .wrapit

; next line
        mov     eax, [.point.y]
        add     eax, [.size.cy]
        add     eax, 2
        invoke  MoveToEx, [.hdc], 2, eax, 0
        jmp     .word2

.wrapit:
        dec     ebx
        jnz     .word2
; can't be fitted - then exit

.finish:
        mov     eax, [.size.cy]
        pop     esi ebx
        return

.outputit:
        lea     eax, [.buffer]
        invoke  MultiByteToWideChar, CP_UTF8, 0, esi, ebx, eax, 1024

        lea     ecx, [.buffer]
        invoke  TextOutW, [.hdc], 0, 0, ecx, eax

        sub     [.count], ebx
        jle     .finish

        lea     esi, [esi+ebx]
        jmp     .wordloop
endp


