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
Source Code: With Syntax Highlighting
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
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
Comment