Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Intellisense: 400th gbSnippet Code Snippet

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • PBWin Intellisense: 400th gbSnippet Code Snippet

    This post provides the source code for an implementation of Intellisense-like
    features. It's also a bit of a celebration - my 400th source code snippet
    added to the PowerBASIC source code library that ships with gbSnippets!

    Two Versions Posted
    There are two versions of the Intellisense code listed below, one that
    includes syntax highlighting and one that does not. Both examples will
    generate a standalone application that demonstrates the Intellisense features
    using a RichEdit control.


    1. Intellisense Without Syntax Highlighting
    This is the simpler of the two, about 300 lines of code. If you're just
    wanting to know how it's done, this is the easier of the two to review.
    If you wanted to use the approach for an autocompletion feature or
    automatic spellchecker, then this will also be the easiest to adapt.

    2. Intellisense + Syntax Highlighting
    This is the longest of the two, coming in at about 500 line of code. If you're
    intersested in working with source code editing, this has the more relevant code.

    The code for both is commented, but in view of complexity of the code and
    the number of procedures used, I provide a lengthy introduction below.

    I expect to have this incorporated into gbSnippets later this week, plus this
    source code will be included as library snippets in the new release.

    Data Files
    There are several text data files needed, available from
    this link as a zipped file. Just unzip the contents and put
    in the folder with the EXE that you get from compiling
    the demo code below.



    In addition to the syntax for PowerBASIC statements, these libraries
    provide the syntax for all of the API declared in the various include files
    that are distributed with PowerBASIC. API are not highlighted like keywords,
    but their syntax (argument list) is available.

    Finally, by default, the variable type declaration of API is not included. But
    type declarations are available by replacing the "word1_sort" text file with
    the "word1_full" text file.

    And by the way - these data files were a very tedious task, so don't
    overlook their contribution to making the Intellisense code work. If you find
    any errors or have suggestions for additions to the code, please let me
    know! After hours of scanning PowerBASIC documentation I won't be
    surprised to find that some tweaks are needed. The good news is that the
    files are simple text files and can be easily updated!

    Intellisense
    I've found gbSnippets, with its easy access to source code, to be very
    useful to me. I use it daily as the go-between me and the PowerBASIC
    IDE. But I've always thought that Intellisense features would really improve
    my productivity - just as my experience with it in VB had shown. Only
    recently, with experience drawn from my released spell check source code,
    did I think it might be much easier to implement than I expected.

    These Intellisense snippets have been the focus of my work on gbSnippets
    for about the last 3 weeks, so I hope the investment in time is as useful to
    my users (or anyone else wanting similar features in their own apps) as I
    expect it will be. I can already see how to add additional features, but I
    think the code is good enough to be useful as-is, so I decided to publish it
    now. As I use the results myself, I'll be working to improve them even
    more.

    AutoCompletion vs Intellisense
    In Microsoft Word, spell check happens as you type each letter, offerring
    suggestions for completing each words as it is typed. This is generally
    referred to as autocompletion.

    With Intellisense the focus is not on completing a keyword that you've
    started to type, but rather on what keywords, structure members or syntax
    are available to follow the word or phrase that has just been typed.

    The snippets below deal with Intellisense - with providing suggestions for
    what follows a word/phrase that has just been typed, not on completing
    the word that is being typed (not that I won't include that in a future
    snippet update).

    Basic Concepts
    In general, when a recognized word/phrase has been typed, its additional
    keywords or structure members are displayed - a popup label for
    syntax/arguments and a popup listbox for member lists.

    As implemented in these snippets, as each letter is typed the letters to the
    left of the caret are scanned for the presence of up to 3 words,
    corresponding to the maximum number of words in a multi-word PowerBASIC
    statement.

    However, the scanning only takes place when the character to the left of
    the cursor is one of several marker characters - a space, open parenthesis,
    period, or close parenthesis. These four characters mark the possible
    start/end of a PowerBASIC word or phrase that Intellisense will recognize.
    The presence of any other character closes any visible Intellisense display
    of information.

    When a marker charater is found and the (up to) 3 words to the left are
    scanned, a prioritized list of 5 word/phrase searches is used to determine
    if the words are found in the Intellisense libraries.

    There are two types of words/phrases - those that will simply be followed
    by a list of arguments (the 'syntax' of a statement) and those which will be
    followed by other keywords, named arguments or structure members.

    The 5 searches use the following 5 libraries, corresponding to the terms
    that will be recognized. The files include the syntax or members associated
    with each of the terms.

    - single word Keywords
    - dual word Keyword phrases
    - triple word Keyword phrases
    - single word Keywords with allowed Member list
    - dual word Keyword phrases with allowed Member list

    These correspond to the PowerBASIC statements, which consist of 1-3
    keywords that preceed an argument list or 1-2 keywords which preceed
    member options.

    If a word/phrase is found that supports trailing arguments (syntax), the
    argument list is presented in a small label just below the cursor. Unlike the
    Microsoft implementation of Intellisense, the sub-label does not bold the
    specific argument currently being typed. I can see how that would be
    accomplished, but in this release the feature is not included.

    Once the cursor moves beyond a marker character, the syntax label is
    removed.

    If a word/phrase is found that supports trailing members, the member list is
    presented in a popup listbox just below the cursor. A user can user the
    arrow keys to select the member to insert into the edit control, then press
    TAB or Enter to insert the selected item into the edit control.

    With an Intellisense member listbox or syntax label shown, the user may
    use the following keys to take action.

    ESC - hide the label/listbox
    TAB - insert the argument list or selected member at the cursor
    ENTER - insert the argument list or selected member and move cursor to a new line

    Procedures
    In addition to code in the callback and subclassed control procedures,
    there are several different subroutines which combine to provide the
    Intellisense features.

    - Intellisense -coordinates all of the other routines
    - CharToLeftOfCursor -returns the single character to the left of the cursor
    - TestLeftChar -takes action depending on what CharToLeftOfCursor Returns
    - WordsToLeft -returns the (up to) 3 words preceeding the cursor
    - CloseIntellisense -hide label/listbox, reset all flags
    - InsertText -place the label/listbox text into the RichEdit control
    - LoadRef -loads the reference files
    - BinaryRefSearch -common routine to search all reference files
    - ModifySyntax -modifies how syntax is displayed, depending on context of user input
    - DisplaySyntaxLabel -shows argument list (syntax) for the preceeding 1-3 words/phrase
    - DisplaySyntaxListBox -shows available Members for the preceeding 1-2 words/phrase
    - NewListBoxProc -detects pressing RETURN, ESC, and TAB keys in ListBox
    - NewRichEditProc -detects pressing RETURN key in RichEdit control

    Since I've previously posted a syntax highlighting, I've not listed the
    various procedures it requires.

    Limitations
    The Intellisense implementation in the compilable example below has the
    following limitations, which may be different than some implementations of
    Intellisense available in other editors.

    1. Dim x as MyType
    This snippet cannot automatically (at run time) determine MyType members
    unless they have been manually placed in the reference files. Variables
    dimensioned as structure types are not recognized, i.e., popup member lists
    are not presented.

    2. ListBox
    Once the listbox is displayed, a selection must be mode by pressing
    TAB or Enter, or else the ESC can be used to removed the listbox from
    view. Pressing letter keys will select an item from the list but the typed
    letters will not appear in the edit control.

    3. Argument Highlighting
    As arguments to a keyword/phrase are typed, Microsoft Intellisense
    changes the content of the popup syntax label - bolding the argument
    currently being typed. The snippets below do not provide the bolding
    feature.

    I'm working on code for all three of these, but the features of this version
    of Intellisense are useful enough that I decided to release the code now.
    I'll post revisions as I make the improvements.

    Source Code: Without Syntax Highlighting
    Code:
    'Compilable Example:
    #Compile Exe
    #Dim All     
    #Include "Win32API.inc"
    #Include "RichEdit.inc"
    #Include "CommCtrl.inc"
    Global Ref_MemTerm1() As String,  Ref_MemMember1() As String
    Global Ref_MemTerm2() As String,  Ref_MemMember2() As String
    Global Ref_Term1() As String, Ref_Desc1() As String, Ref_Syntax1() As String
    Global Ref_Term2() As String, Ref_Desc2() As String, Ref_Syntax2() As String
    Global Ref_Term3() As String, Ref_Desc3() As String, Ref_Syntax3() As String
    Global hDlg as DWord, hRichEdit as DWord, LabelVisible&, ListBoxVisible&, hListBox As DWord
    Global OldListBoxProc&, PI As CharRange, OldRichEditProc&, CancelIntellisense&
    %ID_RichEdit = 501 : %ID_Label = 502 : %ID_ListBox = 503 : %ID_Button = 504 : %ID_Button2 = 505
    Function PBMain() As Long
       Local style&, buf$
       Dim Ref_Term1(0), Ref_Desc1(0), Ref_Syntax1(0)
       Dim Ref_Term2(0), Ref_Desc2(0), Ref_Syntax2(0)
       Dim Ref_Term3(0), Ref_Desc3(0), Ref_Syntax3(0)
       Dim Ref_MemTerm1(0), Ref_MemMember1(0)
       Dim Ref_MemTerm2(0), Ref_MemMember2(0)
       buf$ =  "Type any PowerBASIC code."
       style& = %WS_Child Or %WS_Visible Or %ES_MultiLine Or %WS_VScroll Or %ES_AutoHScroll _
                 Or %WS_HScroll Or %ES_AutoVScroll Or %ES_WantReturn Or %ES_NoHideSel Or %WS_TabStop
       Dialog New Pixels, 0, "Test Code",300,300,300,160, %WS_OverlappedWindow To hDlg
       LoadRef "word1_short.txt", Ref_Term1(), Ref_Syntax1()
       LoadRef "word2_short.txt", Ref_Term2(), Ref_Syntax2()
       LoadRef "word3_short.txt", Ref_Term3(), Ref_Syntax3()
       LoadRef "members1.txt", Ref_memTerm1(), Ref_memMember1()
       LoadRef "members2.txt", Ref_memTerm2(), Ref_memMember2()
       LoadLibrary("riched32.dll") : InitCommonControls
       Control Add Button, hDlg, %ID_Button, "Just for Show",20,10,80,20
       Control Add Button, hDlg, %ID_Button, "Just for Show",110,10,80,20
       Control Add "RichEdit", hDlg, %ID_RichEdit, buf$,20,40,260,100, style&, %WS_EX_ClientEdge
       Control Add Label, hDlg, %ID_Label, "tooltip",60,60,100,15, %WS_Border
       Control Set Color hDlg, %ID_Label, %Black, %RGB_LightYellow
       Control Add ListBox, hDlg, %ID_ListBox, ,60,60,100,100, %WS_Border
       Control Handle hDlg, %ID_RichEdit To hRichEdit
       Control Handle hDlg, %ID_ListBox To hListBox
       SendMessage hRichEdit, %EM_SETEVENTMASK, 0, %ENM_SELCHANGE Or %ENM_CHANGE Or %ENM_Link Or %ENM_KeyEvents
       Dialog Show Modal hDlg Call DlgProc
    End Function
    
    CallBack Function DlgProc() As Long
       Local temp$, P as CharRange
       Select Case CB.Msg
          Case %WM_InitDialog
              PostMessage hRichEdit, %EM_SETSEL, 0, 0   'remove highlighting on startup
              OldRichEditProc& = SetWindowLong(GetDlgItem(hDlg, %ID_RichEdit), %GWL_WndProc, CodePTR(NewRichEditProc))
              OldListBoxProc& = SetWindowLong(GetDlgItem(hDlg, %ID_ListBox), %GWL_WndProc, CodePTR(NewListBoxProc))
              CloseIntellisense
              SetFocus hRichEdit
          Case %WM_Destroy
              SetWindowLong hRichEdit, %GWL_WNDPROC, OldRichEditProc&   'un-subclass
              SetWindowLong hListBox, %GWL_WNDPROC, OldListBoxProc&   'un-subclass
          Case %WM_Notify
              Select Case CB.NmID
                  Case %ID_RichEdit
                      Select Case CB.Nmcode
                         Case %EN_SelChange
                             TestLeftChar
                      End Select
               End Select
        Case %WM_NEXTDLGCTL
            Select Case GetFocus
               Case hRichEdit        'captures TAB in RichEdit
                   If LabelVisible& Or ListBoxVisible& Then 
                      InsertText : Function = 1 : Exit Function
                   End If                
            End Select
        Case %WM_Command
             Select Case CB.Ctl
                Case %ID_RichEdit
                      If CB.Ctlmsg = %EN_SetFocus Then
                         P.cpmin = 0 : P.cpmax = 0 : SendMessage hRichedit, %EM_EXSETSEL, 0, VarPTR(P)   'highlight none 
                      End If
                Case %IdCancel    'pressing Escape
                      Select Case GetFocus    'gets the control which has the focus
                         Case hRichEdit : If LabelVisible& Or ListBoxVisible& Then CloseIntellisense  'ESC pressed in RichEdit
                      End Select
             End Select
       End Select 
    End Function 
    
    Sub TestLeftChar
        Local temp$
        temp$ = CharToLeftOfCursor
        If temp$ = $spc Then
           Intellisense $spc
        ElseIf temp$ = "(" Then
           Intellisense "("
        ElseIf temp$ = ")" Then
           If LabelVisible& Or ListBoxVisible& Then CloseIntellisense
        ElseIf temp$ = "." Then
           Intellisense "."
        Else
           CloseIntellisense
        End If
    End Sub
    
    Function CharToLeftOfCursor() As String
       Local P As CharRange, buf$, T as TextRange
       SendMessage(hRichEdit, %EM_EXGetSel, 0, VarPTR(P))                     'caret position
       T.Chrg.cpmin = P.cpmin-1 : T.Chrg.cpmax = P.cpmax : buf$ = " "
       T.lpstrText = StrPTR(Buf$)
       SendMessage hRichEdit, %EM_GetTextRange, ByVal 0, VarPTR(T)  'get text, specified char range or from selection
       Function = buf$
    End Function  
    
    Function WordsToLeft(w3$, w2$, w1$) As Long
       Local iLine As Long, buf$, iStartPos&, iLineLength&, P As CharRange, iLeft&, iCount&
       SendMessage(hRichEdit, %EM_EXGetSel, 0, VarPTR(P))                     'caret position
       Decr p.cpmin 
       iLine = SendMessage(hRichEdit, %EM_ExLineFromChar, 0, -1)              'current line#
       iStartPos& = SendMessage(hRichEdit, %EM_LineIndex, iLine, 0)            'position of 1st char in current line
       iLineLength& = SendMessage(hRichEdit, %EM_LineLength, iStartPos&, 0)   'length of specified line
       buf$ = Space$(iLineLength&)
       SendMessage(hRichEdit, %EM_GetLine, iLine, StrPTR(buf$))    'text of current line
       w3$ = Mid$(buf$,1,P.cpmin-iStartPos&)                          'text to left of caret
       w3$ = Retain$(w3$, Any Chr$(65 to 90, 97 to 122, 48 to 57, $Spc, "$&#%?!"))
       iCount& = ParseCount(w3$, " ")
       w1$ = Parse$(w3$," ",iCount&)
       w2$ = Parse$(w3$," ",iCount&-1)
       w3$ = Parse$(w3$," ",iCount&-2)
    End Function 
    
    Sub Intellisense(sChar$)
    
       If CancelIntellisense& Then Exit Sub
    
       Local sWord$, sSyntax$, iReturn&, w3$, w2$, w1$
       SendMessage(hRichEdit, %EM_EXGetSel, 0, VarPTR(PI))                     'caret position at start of intellisense
       WordsToLeft(w3$, w2$, w1$)
    
       If Len(w3$) AND BinaryReferenceSearch(Build$(w3$,$spc,w2$,$spc,w1$), iReturn&, Ref_Term3(), Ref_Syntax3()) Then
           '3 word sequence was found
           sWord$ = Build$(w3$,$spc,w2$,$spc,w1$)
           sSyntax$ = Ref_Syntax3(iReturn&)
           DisplaySyntaxLabel (sSyntax$)
       ElseIf Len(w2$) AND BinaryReferenceSearch(Build$(w2$,$spc,w1$), iReturn&, Ref_memTerm2(), Ref_memMember2()) Then
            '2 word sequence was found
           sWord$ = Build$(w2$,$spc,w1$)
           sSyntax$ = Ref_memMember2(iReturn&)
           DisplaySyntaxListbox (sSyntax$)
       ElseIf Len(w2$) AND BinaryReferenceSearch(Build$(w2$,$spc,w1$), iReturn&, Ref_Term2(), Ref_Syntax2()) Then
            '2 word sequence was found
           sWord$ = Build$(w2$,$spc,w1$)
           sSyntax$ = Ref_Syntax2(iReturn&)
           DisplaySyntaxLabel (sSyntax$)
       ElseIf Len(w1$) AND BinaryReferenceSearch(w1$, iReturn&, Ref_memTerm1(), Ref_memMember1()) Then
            '1 word sequence was found
           sWord$ = w1$
           sSyntax$ = Ref_memMember1(iReturn&)
           DisplaySyntaxListBox (sSyntax$)
       ElseIf Len(w1$) AND BinaryReferenceSearch(w1$, iReturn&, Ref_Term1(), Ref_Syntax1()) Then
            '1 word sequence was found
           sWord$ = w1$
           sSyntax$ = ModifySyntax(sChar$, Ref_Syntax1(iReturn&))
           DisplaySyntaxLabel (sSyntax$)
        Else
            'no matches were found
            If LabelVisible& Or ListBoxVisible& Then CloseIntellisense
        End If
    End Sub
    
    Function ModifySyntax (sChar$, ByVal sSyntax$) As String
    '    If sChar$ = " " AND Left$(sSyntax$,1) = "(" Then             'optional way to skip leading (
    '        sSyntax$ = Mid$(sSyntax$, 2, Len(sSyntax$)-2)
        If sChar$ = "(" AND Left$(sSyntax$,1) = "(" Then
           sSyntax$ = Mid$(sSyntax$, 2)         'do not allow ((
        ElseIf sChar$ = "(" AND Left$(sSyntax$,1) <> "(" Then
           sSyntax$ = ""                        'if sChar is (, then sSyntax must also start with (, otherwise, don't show sSyntax
        End If
        Function = sSyntax$
    End Function
    
    Sub CloseIntellisense
            Control Show State hDlg, %ID_Label, %SW_Hide : LabelVisible& = 0
            Control Show State hDlg, %ID_ListBox, %SW_Hide : ListBoxVisible& = 0
    End Sub
    
    Sub DisplaySyntaxLabel(sSyntax$)
          Local P as Point
          Control Set Text hDlg, %ID_Label, sSyntax$                      'put sSyntax in Label
          Control Send hDlg, %ID_RichEdit, %EM_PosFromChar, VarPTR(P), PI.cpmin   'get xy coordinates of caret
          Control Set Loc hDlg, %ID_Label, P.x+25, P.y+60                'assign position of label
          Control Set Size hDlg, %ID_Label, Len(sSyntax$)*7,15
          Control Show State hDlg, %ID_Listbox, %SW_Hide : ListBoxVisible& = 0   'hide listbox
          Control Show State hDlg, %ID_Label, %SW_Show : LabelVisible& = 1                'show label
    End Sub 
    
    Sub DisplaySyntaxListBox(sMembers$)
          Local P as Point, i As Long
          ListBox Reset hDlg, %ID_ListBox
          Dim mList(ParseCount(sMembers$,".")-1) As String
          Parse sMembers$,mList(),"."
          For i = 0 to UBound(mList) : ListBox Insert hDlg, %ID_ListBox, 1, mList(i) : Next i
          Control Send hDlg, %ID_RichEdit, %EM_PosFromChar, VarPTR(P), PI.cpmin  'get xy coordinates of caret
          Control Set Loc hDlg, %ID_ListBox, P.x+25, P.y+60                'assign position of label
          Control Show State hDlg, %ID_Label, %SW_Hide : LabelVisible& = 0       'hide label
          Control Show State hDlg, %ID_ListBox, %SW_Show :ListBoxVisible& = 1       'show listbox
          Control Set Focus hDlg, %ID_ListBox
          ListBox Select hDlg, %ID_ListBox, 1
    End Sub 
    
    Function BinaryReferenceSearch(ByVal sWord As String, iArrayPos&, ArrayTerm() As String, ArraySyntax() As String) As Long
        Local Upper As Long, Lower As Long
        Lower = LBound(ArrayTerm) : Upper = UBound(ArrayTerm) : sWord = LCase$(sWord)
        'test boundary values
        If sWord = ArrayTerm(Lower) Then iArrayPos& = Lower : Function = 1 : Exit Function
        If sWord = ArrayTerm(Upper) Then iArrayPos& = Upper : Function = 1 : Exit Function
        If sWord < ArrayTerm(Lower) Then iArrayPos& = Lower - 1 : Function = 0 : Exit Function
        If sWord > ArrayTerm(Upper) Then iArrayPos& = Upper + 1 : Function = 0 : Exit Function
        'loop through remaining entries until searchterm found, or it's determined that term is not in the array
        Do Until (Upper <= (Lower+1))
            iArrayPos& = (Lower + Upper) / 2
            If sWord > ArrayTerm(iArrayPos&) Then 
               Lower = iArrayPos&
            ElseIf sWord < ArrayTerm(iArrayPos&) Then 
               Upper = iArrayPos&
            Else
               Function = 1 : Exit Function
            End If
        Loop
    End Function
    
    Sub LoadRef (sFile$, ArrayTerm() As String, ArraySyntax() As String)
        'load any of the 5 reference files - all use the same content format   sWord:::::sSyntax
        Local temp$, i As Long
        Open sFile$ For Binary as #1 : Get$ #1, Lof(1), temp$ : Close
        temp$ = RTrim$(temp$,$crlf)
        ReDim ArrayTerm(ParseCount(temp$,$crlf)-1) As String, ArraySyntax(UBound(ArrayTerm)) As String
        Parse temp$,ArrayTerm(),$crlf
        For i = 0 to UBound(ArrayTerm)
            ArraySyntax(i) = Parse$(ArrayTerm(i),":::::", 2)
            ArrayTerm(i) = Parse$(ArrayTerm(i),":::::", 1)
        Next i
    End Sub
    
    Sub InsertText
       Local temp$
       If LabelVisible& Then
          Control Get Text hDlg, %ID_Label To temp$                                 'get text
          temp$ = temp$ + " "
          Control Show State hDlg, %ID_Label, %SW_Hide : LabelVisible& = 0      'hide label
          SendMessage hRichEdit, %EM_ReplaceSel, 0, StrPTR(temp$)                'put text in RichEdit
       ElseIf ListBoxVisible& Then
          CancelIntellisense& = %True
          ListBox Get Text hDlg, %ID_ListBox To temp$                                'get text (selected item)
          temp$ = temp$ + " " 
          Control Show State hDlg, %ID_ListBox, %SW_Hide : ListBoxVisible& = 0   'hide ListBox
          SetFocus hRichEdit
          SendMessage hRichEdit, %EM_EXSetSel, 0, VarPTR(PI)                       'set cursor to new position
          SendMessage hRichEdit, %EM_ReplaceSel, 0, StrPTR(temp$)                  'put text in RichEdit
          CancelIntellisense& = %False
       End If
    End Sub
    
    Function NewListBoxProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
       Select Case Msg
          Case %WM_GETDLGCODE                 'establish control by the RichEdit
               Function = %DLGC_WANTALLKEYS
               Exit Function
          Case %WM_KeyDown      'WM_Char
               Select Case wParam
                  Case %VK_Return
                     InsertText                            'richedit will now have focus
                     keybd_event %VK_Return, 0, 0, 0   'send return key to hRichEdit
                  Case %VK_Escape
                     CancelIntellisense& = %True      'avoids firing Intellisense a second time before this loop is over
                     CloseIntellisense
                     SetFocus hRichEdit
                     SendMessage (hRichEdit, %EM_EXSetSel, 0, VarPTR(PI))
                     CancelIntellisense& = %False
                  Case %VK_Tab
                     InsertText
                     TestLeftChar               'the inserted text may actually be a keyword itself
               End Select
       End Select
       Function = CallWindowProc(OldListBoxProc&, hWnd, Msg, wParam, lParam)
    End Function
    
    Function NewRichEditProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
       Local iResult&
       Select Case Msg
          Case %WM_KeyDown    'WM_Char
               Select Case wParam
                  Case %VK_Return
                     If LabelVisible& Or ListBoxVisible& Then InsertText   'allow to continue processing
               End Select
       End Select
       Function = CallWindowProc(OldRichEditProc&, hWnd, Msg, wParam, lParam)
    End Function
    
    'gbs_00400
    Source Code: With Syntax Highlighting
    Code:
    'Compilable Example:
    #Compile Exe
    #Dim All
    #Include "Win32api.inc"
    #Include "commctrl.inc"
    #Include "richedit.inc"
    Global LWords() As String, UWords() As String, MWords() As String 
    
    Global hRichEdit As DWord, hDlg As DWord, OrigRichEditProc&, CodeCase&
    Global Ref_MemTerm1() As String,  Ref_MemMember1() As String
    Global Ref_MemTerm2() As String,  Ref_MemMember2() As String
    Global Ref_Term1() As String, Ref_Desc1() As String, Ref_Syntax1() As String
    Global Ref_Term2() As String, Ref_Desc2() As String, Ref_Syntax2() As String
    Global Ref_Term3() As String, Ref_Desc3() As String, Ref_Syntax3() As String
    Global hDlg as DWord, hRichEdit as DWord, LabelVisible&, ListBoxVisible&, hListBox As DWord
    Global OldListBoxProc&, PI As CharRange, OldRichEditProc&, CancelIntellisense&, cp as Long
    %ID_RichEdit = 501 : %ID_Label = 502 : %ID_ListBox = 503 : %ID_Button = 504 : %ID_Button2 = 505
    
    
    Function PBMain() As Long
        'create some sample content for the RichEdit control
        Dim Content$
        Content$ = "Function Example" + $CrLf + "Select Case MyVar" + $CrLf + "Case 12 '1st case" + $CrLf + "End Select" + $CrLf + "End Function"
        Content$ = Content$ + $CrLf + "For i = 1 to 10" + $CrLf + "Incr i" + $CrLf + "Next i"
        Content$ = Content$ + $CrLf + "If x = 2 Then" + $CrLf + "'do nothing" + $CrLf + "End If"
        Dialog New Pixels, 0, "Syntax Test",300,300,600,400, %WS_OverlappedWindow To hDlg
        Dim Ref_Term1(0), Ref_Desc1(0), Ref_Syntax1(0)
        Dim Ref_Term2(0), Ref_Desc2(0), Ref_Syntax2(0)
        Dim Ref_Term3(0), Ref_Desc3(0), Ref_Syntax3(0)
        Dim Ref_MemTerm1(0), Ref_MemMember1(0)
        Dim Ref_MemTerm2(0), Ref_MemMember2(0)
        LoadRef "word1_short.txt", Ref_Term1(), Ref_Syntax1()
        LoadRef "word2_short.txt", Ref_Term2(), Ref_Syntax2()
        LoadRef "word3_short.txt", Ref_Term3(), Ref_Syntax3()
        LoadRef "members1.txt", Ref_memTerm1(), Ref_memMember1()
        LoadRef "members2.txt", Ref_memTerm2(), Ref_memMember2()
        cp = 1
    
        'create RichEdit and subclass (to intercept %WM_KeyUp actions)
        LoadLibrary("riched32.dll")
        InitCommonControls
        Control Add Option, hDlg, 201, "Upper", 10, 10, 50, 20
        Control Add Option, hDlg, 202, "Lower", 90, 10, 50, 20
        Control Add Option, hDlg, 203, "Mixed", 170, 10, 50, 20
        Control Add "RichEdit", hDlg, %ID_RichEdit, Content$, 10, 40, 150, 100, _
                 %WS_Child Or %WS_Visible Or %ES_MultiLine Or %WS_VScroll Or %ES_AutoHScroll _
                 Or %WS_HScroll Or %ES_AutoVScroll Or %ES_WantReturn Or %ES_NoHideSel, _
                 %WS_Ex_ClientEdge
        Control Handle hDlg, %ID_RichEdit To hRichEdit
        SetFont
        OrigRichEditProc& = SetWindowLong(GetDlgItem(hDlg, %ID_RichEdit), %GWL_WndProc, CodePTR(NewRichEditProc))
        SendMessage hRichEdit, %EM_SETEVENTMASK, 0, %ENM_SELCHANGE Or %ENM_CHANGE Or %ENM_Link Or %ENM_KeyEvents
    
       Control Add Label, hDlg, %ID_Label, "tooltip",60,60,100,15, %WS_Border
       Control Set Color hDlg, %ID_Label, %Black, %RGB_LightYellow
       Control Add ListBox, hDlg, %ID_ListBox, ,60,60,100,100, %WS_Border
       Control Handle hDlg, %ID_ListBox To hListBox
    
        Dialog Show Modal hDlg Call DlgProc
    End Function
    
    CallBack Function DlgProc() As Long
       Local P as CharRange
       Select Case CB.Msg
            Case %WM_InitDialog
                CodeCase& = 1        'upper lower mixed
                Control Set Option hDlg, 201, 201, 203
                synInitializeRWords
                synApplySyntax
                OldListBoxProc& = SetWindowLong(GetDlgItem(hDlg, %ID_ListBox), %GWL_WndProc, CodePTR(NewListBoxProc))
                CloseIntellisense
            Case %WM_Size
                Dim w As Long, h As Long
                Dialog Get Client CB.Hndl To w,h
                Control Set Size CB.Hndl, %ID_RichEdit, w-20, h-20
            Case %WM_NEXTDLGCTL
                Select Case GetFocus
                   Case hRichEdit        'captures TAB in RichEdit
                       If LabelVisible& Or ListBoxVisible& Then 
                          InsertText : Function = 1 : Exit Function
                       End If                
                End Select
            Case %WM_Notify
                Select Case CB.NmID
                    Case %ID_RichEdit
                        Select Case CB.Nmcode
                           Case %EN_SelChange
                               TestLeftChar
                        End Select
                 End Select
            Case %WM_Command
                Select Case CB.Ctl
                    Case 201 : CodeCase& = 1 : synApplySyntax
                    Case 202 : CodeCase& = 2 : synApplySyntax
                    Case 203 : CodeCase& = 3 : synApplySyntax
                    Case %ID_RichEdit
                        Select Case CB.Ctlmsg
                           Case %EN_SetFocus
                               P.cpmin = 0 : P.cpmax = 0 : SendMessage hRichedit, %EM_EXSETSEL, 0, VarPTR(P)   'highlight none 
                        End Select
                   Case 100
                      If CB.Ctlmsg = %BN_Clicked Then
                         Local iResult1&, iResult2&
                         TurnOffCol(Cbhndl)
                         ScanLine(0, SendMessage(hRichEdit, %EM_GETLINECOUNT, 0, 0) - 1)
                      End If
                   Case %IdCancel    'pressing Escape
                         Select Case GetFocus    'gets the control which has the focus
                            Case hRichEdit : If LabelVisible& Or ListBoxVisible& Then CloseIntellisense  'ESC pressed in RichEdit
                         End Select
                End Select
        End Select
    End Function
    
    Function NewRichEditProc(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
      Select Case wMsg
         Case %WM_KeyUp         'trap key up, for syntax color check while editing
            Local CurLine As Long
            CurLine = SendMessage(hRichEdit, %EM_EXLINEFROMCHAR, 0, -1)
            ScanLine(CurLine, CurLine)               'check current line only
            Dialog Redraw hDlg
            Function = 0 : Exit Function                  'return zero
          Case %WM_KeyDown 
               Select Case wParam
                  Case %VK_Return
                     If LabelVisible& Or ListBoxVisible& Then InsertText   'allow to continue processing
               End Select
      End Select
      NewRichEditProc = CallWindowProc(OrigRichEditProc&, hWnd, wMsg, wParam, lParam)
    End Function
    
    Sub synApplySyntax()
      MousePTR 11                   'Scan all lines
      TurnOffCol(hDlg)
      ScanLine(0, SendMessage(hRichEdit, %EM_GETLINECOUNT, 0, 0) - 1)
      MousePTR 0
      SetFocus hRichEdit
    End Sub
    
    Sub synInitializeRWords
       Local temp$, i As Long
       ReDim UWords(1000), LWords(1000), MWords(1000)
       'read the language file
       Open Exe.Path$ + "powerbasic.syn" For Input As #1
       While IsFalse Eof(1)
          Line Input #1, temp$
          If Len(Trim$(temp$)) Then
             MWords(i) = temp$
             UWords(i) = UCase$(MWords(i))
             LWords(i) = LCase$(MWords(i))
             Incr i
             End If
        Wend
        Close #1
        ReDim Preserve UWords(i-1), LWords(i-1), MWords(i-1)
    End Sub 
    
    Function setRichTextColor( ByVal NewColor As Long) As Long
    ' setRichTextColor sets the textcolor for selected text in a Richedit control.
    ' &HFF - read, &HFF0000 - blue, &H008000 - dark green, &H0 is black, etc.
       Local cf As CHARFORMAT
       cf.cbSize      = Len(cf)       'Length of structure
       cf.dwMask      = %CFM_COLOR    'Set mask to colors only
       cf.crTextColor = NewColor      'Set the new color value
       SendMessage(hRichEdit, %EM_SETCHARFORMAT, %SCF_SELECTION, VarPTR(cf))
    End Function
    
    Sub TurnOffCol(ByVal hDlg As Long)
    ' Set all text to black - faster this way
      Local cf As CHARFORMAT, xEvent As Long
      Local tTime As Single : tTime = Timer                      'get time
      xEvent = SendMessage(hRichEdit, %EM_GETEVENTMASK, 0, 0)         'Get eventmask
      SendMessage(hRichEdit, %EM_SETEVENTMASK, 0, 0)            'Disable eventmask
      MousePTR 11                                                'Hourglass
      cf.cbSize      = Len(cf)                                   'Length of structure
      cf.dwMask      = %CFM_COLOR                                'Set mask to colors only
      cf.crTextColor = &H0                                       'Set black color value
      SendMessage(hRichEdit, %EM_SETCHARFORMAT, -1, VarPTR(cf)) '%SCF_ALL = -1
      If xEvent Then
         SendMessage(hRichEdit, %EM_SETEVENTMASK, 0, xEvent)     'Enable eventmask
      End If                                                     'Arrow
      MousePTR 0
      SendMessage(hRichEdit, %EM_SETMODIFY, %FALSE, 0)          'reset modify flag
    End Sub
    
    Sub ScanLine(ByVal Line1 As Long, ByVal Line2 As Long)
    ' Syntax color parser for received line numbers
      Local pd As CHARRANGE, Oldpd As CHARRANGE, tBuff As TEXTRANGE
      Local xWord As String, Buf As String
      Local Aspect As Long, xEvents As Long, I As Long , J As Long, stopPos As Long
      Local lnLen As Long, Result As Long, wFlag As Byte, Letter As Byte Ptr
    
      SendMessage(hRichEdit, %EM_EXGETSEL, 0, VarPTR(Oldpd)) 'Original position
                                                              '(so we can reset it later)
      'Disable the event mask, for better speed
      xEvents = SendMessage(hRichEdit, %EM_GETEVENTMASK, 0, 0)
      SendMessage(hRichEdit, %EM_SETEVENTMASK, 0, 0)
    
      'Turn off redraw for faster and smoother action
      SendMessage(hRichEdit, %WM_SETREDRAW, 0, 0)
    
      If Line1 <> Line2 Then                                  'if multiple lines
         MousePTR 11
      Else                                                                     'editing a line
         pd.cpMin = SendMessage(hRichEdit, %EM_LINEINDEX, Line1, 0)                'line start
         pd.cpMax = pd.cpMin + SendMessage(hRichEdit, %EM_LINELENGTH, pd.cpMin, 0) 'line end
         SendMessage(hRichEdit, %EM_EXSETSEL, 0, VarPTR(pd))                  'select line
         setRichTextColor &H0                                             'set black
      End If
    
      For J = Line1 To Line2
         Aspect = SendMessage(hRichEdit, %EM_LINEINDEX, J, 0)       'line start
         lnLen  = SendMessage(hRichEdit, %EM_LINELENGTH, Aspect, 0) 'line length
    
         If lnLen Then
            Buf = Space$(lnLen + 1)
            tBuff.chrg.cpMin = Aspect
            tBuff.chrg.cpMax = Aspect + lnLen
            tBuff.lpstrText = StrPTR(Buf)
            lnLen = SendMessage(hRichEdit, %EM_GETTEXTRANGE, 0, ByVal VarPTR(tBuff)) 'Get line
    
            CharUpperBuff(ByVal StrPTR(Buf), lnLen)        'Make UCASE
            'I always use this one, since it handles characters > ASC(127) as well.. ;-)
    
            '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            ' Loop through the line, using a pointer for better speed
            '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Letter = StrPTR(Buf) : wFlag = 0
            For I = 1 To Len(Buf)
               Select Case @Letter 'The characters we need to inlude in a word
                  Case 97 To 122, 65 To 90, 192 To 214, 216 To 246, 248 To 255, _
                                                     35 To 38, 48 To 57, 63, 95
                     If wFlag = 0 Then
                        wFlag = 1 : stopPos = I
                     End If
    
                  Case 34 ' string quotes -> "
                    stopPos = Instr(I + 1, Buf, Chr$(34)) 'Find match
                    If stopPos Then
                       pd.cpMin = Aspect + I
                       pd.cpMax = Aspect + stopPos - 1
                       SendMessage(hRichEdit, %EM_EXSETSEL, 0, VarPTR(pd))
                       setRichTextColor &HFF
                       StopPos = (StopPos - I + 1)
                       I = I + StopPos
                       Letter = Letter + StopPos
                       wFlag = 0
                    End If
    
                  Case 39 ' uncomment character -> '
                     pd.cpMin = Aspect + I - 1
                     pd.cpMax = Aspect + lnLen
                     SendMessage(hRichEdit, %EM_EXSETSEL, 0, VarPTR(pd))
                     setRichTextColor &H00008000&
                     wFlag = 0
                     Exit For
    
                  Case Else  'word is ready
                     If wFlag = 1 Then
                        xWord = Mid$(Buf, stopPos, I - stopPos)  'Get word
    
                        If xWord = "REM" Then  'extra for the uncomment word, REM
                           pd.cpMin = Aspect + I - Len(xWord) - 1
                           pd.cpMax = Aspect + lnLen
                           SendMessage(hRichEdit, %EM_EXSETSEL, 0, VarPTR(pd))
                           setRichTextColor &H00008000&
                           wFlag = 0
                           Exit For
                        End If
                        Array Scan UWords(0), = xWord, To Result  'Is it in the array?
                        If Result Then
                           pd.cpMin = Aspect + stopPos - 1
                           pd.cpMax = Aspect + I - 1
    '---------------------------------upper/lower/mixed handled here-----------
                        SendMessage(hRichEdit, %EM_EXSETSEL, 0, VarPTR(pd))
                        If CodeCase& Then
                           xWord = Choose$(CodeCase&, UWords(Result-1), LWords(Result-1), MWords(Result-1))
                           Control Send hDlg, %ID_RichEdit, %EM_ReplaceSel, %True, StrPTR(xWord)
                        End If 
    '----------------------------------------------------------------------
                           SendMessage(hRichEdit, %EM_EXSETSEL, 0, VarPTR(pd))
                           setRichTextColor(&HFF0000)       'set blue color
                        End If
                        wFlag = 0
                     End If
               End Select
    
               Incr Letter
            Next I
         End If
      Next J
    
      'Reset original caret position
      SendMessage(hRichEdit, %EM_EXSETSEL, 0, VarPTR(Oldpd))
    
      'Turn on Redraw again and refresh - this one causes some flicker in Richedit..
      SendMessage hRichEdit, %WM_SETREDRAW, 1, 0
      InvalidateRect hRichEdit, ByVal %NULL, 0 : UpdateWindow hRichEdit
    
      'Reset the event mask
      If xEvents Then SendMessage(hRichEdit, %EM_SETEVENTMASK, 0, xEvents)
    End Sub
    
    Sub SetFont
       Dim hFont As DWord
       Font New "Comic Sans MS", 10, 1 To hFont
       Control Set Font hDlg, %ID_RichEdit, hFont
    End Sub
    
    Sub TestLeftChar
        Local temp$
        temp$ = CharToLeftOfCursor
        If temp$ = $spc Then
           Intellisense $spc
        ElseIf temp$ = "(" Then
           Intellisense "("
        ElseIf temp$ = ")" Then
           If LabelVisible& Or ListBoxVisible& Then CloseIntellisense
        ElseIf temp$ = "." Then
           Intellisense "."
        Else
           CloseIntellisense
        End If
    End Sub
    
    Function CharToLeftOfCursor() As String
       Local P As CharRange, buf$, T as TextRange
       SendMessage(hRichEdit, %EM_EXGetSel, 0, VarPTR(P))                     'caret position
       T.Chrg.cpmin = P.cpmin-1 : T.Chrg.cpmax = P.cpmax : buf$ = " "
       T.lpstrText = StrPTR(Buf$)
       SendMessage hRichEdit, %EM_GetTextRange, ByVal 0, VarPTR(T)  'get text, specified char range or from selection
       Function = buf$
    End Function  
    
    Function WordsToLeft(w3$, w2$, w1$) As Long
       Local iLine As Long, buf$, iStartPos&, iLineLength&, P As CharRange, iLeft&, iCount&
       SendMessage(hRichEdit, %EM_EXGetSel, 0, VarPTR(P))                     'caret position
       Decr p.cpmin 
       iLine = SendMessage(hRichEdit, %EM_ExLineFromChar, 0, -1)              'current line#
       iStartPos& = SendMessage(hRichEdit, %EM_LineIndex, iLine, 0)            'position of 1st char in current line
       iLineLength& = SendMessage(hRichEdit, %EM_LineLength, iStartPos&, 0)   'length of specified line
       buf$ = Space$(iLineLength&)
       SendMessage(hRichEdit, %EM_GetLine, iLine, StrPTR(buf$))    'text of current line
       w3$ = Mid$(buf$,1,P.cpmin-iStartPos&)                          'text to left of caret
       w3$ = Retain$(w3$, Any Chr$(65 to 90, 97 to 122, 48 to 57, $Spc, "$&#%?!"))
       iCount& = ParseCount(w3$, " ")
       w1$ = Parse$(w3$," ",iCount&)
       w2$ = Parse$(w3$," ",iCount&-1)
       w3$ = Parse$(w3$," ",iCount&-2)
    End Function 
    
    Sub Intellisense(sChar$)
       If CancelIntellisense& Then Exit Sub
    
       Local sWord$, sSyntax$, iReturn&, w3$, w2$, w1$
       SendMessage(hRichEdit, %EM_EXGetSel, 0, VarPTR(PI))                     'caret position at start of intellisense
       WordsToLeft(w3$, w2$, w1$)
    
       If Len(w3$) AND BinaryReferenceSearch(Build$(w3$,$spc,w2$,$spc,w1$), iReturn&, Ref_Term3(), Ref_Syntax3()) Then
           '3 word sequence was found
           sWord$ = Build$(w3$,$spc,w2$,$spc,w1$)
           sSyntax$ = Ref_Syntax3(iReturn&)
           DisplaySyntaxLabel (sSyntax$)
       ElseIf Len(w2$) AND BinaryReferenceSearch(Build$(w2$,$spc,w1$), iReturn&, Ref_memTerm2(), Ref_memMember2()) Then
            '2 word sequence was found
           sWord$ = Build$(w2$,$spc,w1$)
           sSyntax$ = Ref_memMember2(iReturn&)
           DisplaySyntaxListbox (sSyntax$)
       ElseIf Len(w2$) AND BinaryReferenceSearch(Build$(w2$,$spc,w1$), iReturn&, Ref_Term2(), Ref_Syntax2()) Then
            '2 word sequence was found
           sWord$ = Build$(w2$,$spc,w1$)
           sSyntax$ = Ref_Syntax2(iReturn&)
           DisplaySyntaxLabel (sSyntax$)
       ElseIf Len(w1$) AND BinaryReferenceSearch(w1$, iReturn&, Ref_memTerm1(), Ref_memMember1()) Then
            '1 word sequence was found
           sWord$ = w1$
           sSyntax$ = Ref_memMember1(iReturn&)
           DisplaySyntaxListBox (sSyntax$)
       ElseIf Len(w1$) AND BinaryReferenceSearch(w1$, iReturn&, Ref_Term1(), Ref_Syntax1()) Then
            '1 word sequence was found
           sWord$ = w1$
           sSyntax$ = ModifySyntax(sChar$, Ref_Syntax1(iReturn&))
           DisplaySyntaxLabel (sSyntax$)
        Else
            'no matches were found
            If LabelVisible& Or ListBoxVisible& Then CloseIntellisense
        End If
    End Sub
    
    Function ModifySyntax (sChar$, ByVal sSyntax$) As String
    '    If sChar$ = " " AND Left$(sSyntax$,1) = "(" Then             'optional way to skip leading (
    '        sSyntax$ = Mid$(sSyntax$, 2, Len(sSyntax$)-2)
        If sChar$ = "(" AND Left$(sSyntax$,1) = "(" Then
           sSyntax$ = Mid$(sSyntax$, 2)         'do not allow ((
        ElseIf sChar$ = "(" AND Left$(sSyntax$,1) <> "(" Then
           sSyntax$ = ""                        'if sChar is (, then sSyntax must also start with (, otherwise, don't show sSyntax
        End If
        Function = sSyntax$
    End Function
    
    Sub CloseIntellisense
            Control Show State hDlg, %ID_Label, %SW_Hide : LabelVisible& = 0
            Control Show State hDlg, %ID_ListBox, %SW_Hide : ListBoxVisible& = 0
    End Sub
    
    Sub DisplaySyntaxLabel(sSyntax$)
          Local P as Point
          Control Set Text hDlg, %ID_Label, sSyntax$                      'put sSyntax in Label
          Control Send hDlg, %ID_RichEdit, %EM_PosFromChar, VarPTR(P), PI.cpmin   'get xy coordinates of caret
          Control Set Loc hDlg, %ID_Label, P.x+25, P.y+60                'assign position of label
          Control Set Size hDlg, %ID_Label, Len(sSyntax$)*7,15
          Control Show State hDlg, %ID_Listbox, %SW_Hide : ListBoxVisible& = 0   'hide listbox
          Control Show State hDlg, %ID_Label, %SW_Show : LabelVisible& = 1                'show label
    End Sub 
    
    Sub DisplaySyntaxListBox(sMembers$)
          Local P as Point, i As Long
          ListBox Reset hDlg, %ID_ListBox
          Dim mList(ParseCount(sMembers$,".")-1) As String
          Parse sMembers$,mList(),"."
          For i = 0 to UBound(mList) : ListBox Insert hDlg, %ID_ListBox, 1, mList(i) : Next i
          Control Send hDlg, %ID_RichEdit, %EM_PosFromChar, VarPTR(P), PI.cpmin  'get xy coordinates of caret
          Control Set Loc hDlg, %ID_ListBox, P.x+25, P.y+60                'assign position of label
          Control Show State hDlg, %ID_Label, %SW_Hide : LabelVisible& = 0       'hide label
          Control Show State hDlg, %ID_ListBox, %SW_Show :ListBoxVisible& = 1       'show listbox
          Control Set Focus hDlg, %ID_ListBox
          ListBox Select hDlg, %ID_ListBox, 1
    End Sub 
    
    Function BinaryReferenceSearch(ByVal sWord As String, iArrayPos&, ArrayTerm() As String, ArraySyntax() As String) As Long
        Local Upper As Long, Lower As Long
        Lower = LBound(ArrayTerm) : Upper = UBound(ArrayTerm) : sWord = LCase$(sWord)
        'test boundary values
        If sWord = ArrayTerm(Lower) Then iArrayPos& = Lower : Function = 1 : Exit Function
        If sWord = ArrayTerm(Upper) Then iArrayPos& = Upper : Function = 1 : Exit Function
        If sWord < ArrayTerm(Lower) Then iArrayPos& = Lower - 1 : Function = 0 : Exit Function
        If sWord > ArrayTerm(Upper) Then iArrayPos& = Upper + 1 : Function = 0 : Exit Function
        'loop through remaining entries until searchterm found, or it's determined that term is not in the array
        Do Until (Upper <= (Lower+1))
            iArrayPos& = (Lower + Upper) / 2
            If sWord > ArrayTerm(iArrayPos&) Then 
               Lower = iArrayPos&
            ElseIf sWord < ArrayTerm(iArrayPos&) Then 
               Upper = iArrayPos&
            Else
               Function = 1 : Exit Function
            End If
        Loop
    End Function
    
    Sub LoadRef (sFile$, ArrayTerm() As String, ArraySyntax() As String)
        'load any of the 5 reference files - all use the same content format   sWord:::::sSyntax
        Local temp$, i As Long
        Open sFile$ For Binary as #1 : Get$ #1, Lof(1), temp$ : Close
        temp$ = RTrim$(temp$,$crlf)
        ReDim ArrayTerm(ParseCount(temp$,$crlf)-1) As String, ArraySyntax(UBound(ArrayTerm)) As String
        Parse temp$,ArrayTerm(),$crlf
        For i = 0 to UBound(ArrayTerm)
            ArraySyntax(i) = Parse$(ArrayTerm(i),":::::", 2)
            ArrayTerm(i) = Parse$(ArrayTerm(i),":::::", 1)
        Next i
    End Sub
    
    Sub InsertText
       Local temp$
       If LabelVisible& Then
          Control Get Text hDlg, %ID_Label To temp$                                 'get text
          temp$ = temp$ + " "
          Control Show State hDlg, %ID_Label, %SW_Hide : LabelVisible& = 0      'hide label
          SendMessage hRichEdit, %EM_ReplaceSel, 0, StrPTR(temp$)                'put text in RichEdit
       ElseIf ListBoxVisible& Then
          CancelIntellisense& = %True
          ListBox Get Text hDlg, %ID_ListBox To temp$                                'get text (selected item)
          temp$ = temp$ + " " 
          Control Show State hDlg, %ID_ListBox, %SW_Hide : ListBoxVisible& = 0   'hide ListBox
          SetFocus hRichEdit
          SendMessage hRichEdit, %EM_EXSetSel, 0, VarPTR(PI)                       'set cursor to new position
          SendMessage hRichEdit, %EM_ReplaceSel, 0, StrPTR(temp$)                  'put text in RichEdit
          CancelIntellisense& = %False
       End If
    End Sub
    
    Function NewListBoxProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
       Select Case Msg
          Case %WM_GETDLGCODE                 'establish control by the RichEdit
               Function = %DLGC_WANTALLKEYS
               Exit Function
          Case %WM_KeyDown      'WM_Char
               Select Case wParam
                  Case %VK_Return
                     InsertText                            'richedit will now have focus
                     keybd_event %VK_Return, 0, 0, 0   'send return key to hRichEdit
                  Case %VK_Escape
                     CancelIntellisense& = %True      'avoids firing Intellisense a second time before this loop is over
                     CloseIntellisense
                     SetFocus hRichEdit
                     SendMessage (hRichEdit, %EM_EXSetSel, 0, VarPTR(PI))
                     CancelIntellisense& = %False
                  Case %VK_Tab
                     InsertText
                     TestLeftChar               'the inserted text may actually be a keyword itself
               End Select
       End Select
       Function = CallWindowProc(OldListBoxProc&, hWnd, Msg, wParam, lParam)
    End Function
    
    'gbs_00404
    Last edited by Gary Beene; 3 Nov 2009, 08:17 AM.

  • #2
    Very nice, Gary.

    I think you’d find a “Trie” interesting for your project.
    Thousands, if not millions, of times faster.

    A Trie is a radix type structure; words lay on top of each other.
    So, “cat” is embedded “cats“.
    Every word starting with “ca” is in the tree below “ca”.

    So, you get instant prefix search.
    Typing, “arr” could bring up every “array “ command.

    In a Trie, the match is done one character at a time.
    As you test each character in the key word, you’re moving down the tree.

    So, if the key word starts with “a”, on the first character you’ve eliminated every word that doesn’t begin with “a”. All keys starting with “a” are in a tree branch below “a”.

    I’ll post an updated version of a Trie Tree.
    (just starting on it)
    Previous version was fast enough to find 100,000 keys out of 100,000 in less then 0.02 seconds.

    Give it a look because it could add speed and prefix search.

    I have been interested in Intellisense but have been put off by the complexity of Rich Text.
    stanthemanstan~gmail
    Dead Theory Walking
    Range Trie Tree
    HLib ~ Free Data Container Lib ~ Arrays, Lists, Stacks, Queues, Deques, Trees, Hashes

    Comment

    Working...
    X