Scintilla + AutoComplete

Everything else that doesn't fall into one of the other PB categories.
AZJIO
Addict
Addict
Posts: 1318
Joined: Sun May 14, 2017 1:48 am

Scintilla + AutoComplete

Post by AZJIO »

Example.
The start was taken from here
Download (AutoComplete.pb + keyword.txt + Snippet\For.pb)
Tried to use FindData.pbi, but in this case it is required to enter case-sensitive letters.

Code: Select all

EnableExplicit
InitScintilla()

Define hdr$, hdrLen
;- Global
Global *buffer
Global autocompTypeChar =  ':'
Global typ$ = Chr(autocompTypeChar)
Global autocompSeparatorChar =  #LF
Global sep$ = Chr(autocompSeparatorChar)
Global *autocompAutoCanceledByCharacters = UTF8(";,' .")
; Global *autocompAutoSelectedByCharacters = UTF8("|({")
Global autocompList.s = sep$ + "For" + typ$ + 0 + sep$ + ;type=0 (just for test)
                        "If" + typ$ + 1 + sep$ + ;type=1 (just for test)
                        "Next" + sep$ +
                        "Str" + sep$ +
                        "Ter" + #DQUOTE$ + sep$ +
                        "Web" + sep$
Global *autocompList

Global Format, length, bytes
CompilerIf #PB_Compiler_Debugger
	Global SnippetPath$ = #PB_Compiler_FilePath + "Snippet\"
	Global KeywordPath$ = #PB_Compiler_FilePath + "keyword.txt"
CompilerElse
	Global SnippetPath$ = GetPathPart(ProgramFilename()) + "Snippet\"
	Global KeywordPath$ = GetPathPart(ProgramFilename()) + "keyword.txt"
CompilerEndIf

Debug SnippetPath$
Debug KeywordPath$
#File = 0
If ReadFile(#File, KeywordPath$, #PB_UTF8)
; 	Format = ReadStringFormat(#File)
; 	If Format = #PB_UTF8
		length = Lof(#File)
		If length
			*autocompList = AllocateMemory(length)
			If *autocompList
				bytes = ReadData(#File, *autocompList, length)
				autocompList = PeekS(*autocompList, -1, #PB_UTF8)
; 				*autocompList = @autocompList
; 				FreeMemory(*autocompList)
			EndIf
		Else
			*autocompList = UTF8(autocompList)
		EndIf
; 	EndIf
	CloseFile(#File)
Else
	*autocompList = UTF8(autocompList)
EndIf



Procedure.s GetTextFile(Path$)
	Protected Result$, *mem
	#File = 0
	If ReadFile(#File, Path$, #PB_UTF8)
	; 	Format = ReadStringFormat(#File)
	; 	If Format = #PB_UTF8
			length = Lof(#File)
			If length
				*mem = AllocateMemory(length)
				If *mem
					bytes = ReadData(#File, *mem, length)
					Result$ = PeekS(*mem, bytes, #PB_UTF8)
					FreeMemory(*mem)
				EndIf
			EndIf
	; 	EndIf
		CloseFile(#File)
	Else
		Result$ = GetFilePart(Path$, #PB_FileSystem_NoExtension)
	EndIf
	
    ProcedureReturn Result$
EndProcedure


ProcedureDLL AutoCompletionScintillaCB(Gadget, *scinotify.SCNotification)
	Protected Pos, WordStart, autocompSearchLength, *pos, PosFind, PosFind2, *DestinationMemoryID, tmp$;, range.TextRange
	Protected tmp, word$, length
	
	With *scinotify

		If \nmhdr\code = #SCN_CHARADDED
			Select \ch
				Case 'a' To 'z', 'A' To 'Z';, '0' To '9'
					Pos = ScintillaSendMessage(0, #SCI_GETCURRENTPOS) ; возвращает текущую позицию.
					WordStart = ScintillaSendMessage(0, #SCI_WORDSTARTPOSITION, Pos, 1) ; сообщения возвращают начало слова
					autocompSearchLength = Pos - WordStart
					
					*pos = ScintillaSendMessage(0, #SCI_GETCHARACTERPOINTER) ; прямой доступ
					word$ = PeekS(*pos + WordStart, autocompSearchLength, #PB_UTF8) ; #PB_ByteLength
					word$ = sep$ + word$
					PosFind = FindString(autocompList, word$, 1, #PB_String_NoCase)
					If PosFind
						Repeat
							tmp = PosFind2
							PosFind2 =FindString(autocompList, word$, PosFind2 + 1, #PB_String_NoCase)
						Until PosFind2 = 0
						If tmp
							PosFind2 = FindString(autocompList, sep$, tmp + 1)
							If PosFind2
; 								Debug word$
; 								Debug Str(PosFind) + " " + Str(PosFind2) + " " + Str(PosFind2 - PosFind)
; 								если позиции не изменяются надо не перерисовывать список
								*DestinationMemoryID = AllocateMemory(PosFind2 - PosFind - 1)
								CopyMemory(*autocompList + PosFind, *DestinationMemoryID , PosFind2 - PosFind - 1)
								ScintillaSendMessage(0, #SCI_AUTOCSHOW, autocompSearchLength, *DestinationMemoryID)
								FreeMemory(*DestinationMemoryID)
							EndIf
						EndIf
; 					Else
; 						ScintillaSendMessage(0, #SCI_AUTOCCANCEL)
					EndIf
			EndSelect
; 		EndIf
		ElseIf \nmhdr\code = #SCN_AUTOCSELECTION ; удалите сообщение если нужно вставить ключевое слово без фрагмента
			ScintillaSendMessage(0, #SCI_AUTOCCANCEL) ; омена вставки
			; Debug PeekS(\text, -1, #PB_UTF8)
			tmp$ = PeekS(\text, -1, #PB_UTF8)
			tmp = Len(tmp$)
			tmp$ = GetTextFile(SnippetPath$ + tmp$ + ".pb")
			length = Len(tmp$)
			*DestinationMemoryID = UTF8(tmp$)
			Pos = ScintillaSendMessage(0, #SCI_GETCURRENTPOS) ; возвращает текущую позицию.
			WordStart = ScintillaSendMessage(0, #SCI_WORDSTARTPOSITION, Pos, 1) ; сообщения возвращают начало слова
			ScintillaSendMessage(0, #SCI_DELETERANGE, WordStart, Pos - WordStart) ; удалить введённое слово
; 			вставка фрагментов из одноимённого файла. Вместо вставки слова прочитать одноимённый файл и вставить его содержимое.
			ScintillaSendMessage(0, #SCI_INSERTTEXT, -1, *DestinationMemoryID)
			FreeMemory(*DestinationMemoryID)
			If length = tmp
				Pos + length - 1
			Else
				Pos + length
			EndIf
			ScintillaSendMessage(0, #SCI_GOTOPOS, Pos)
; 		ElseIf \nmhdr\code = #SCN_MODIFIED And \modificationType & #SC_MOD_DELETETEXT
		ElseIf \nmhdr\code = #SCN_AUTOCCHARDELETED ; если удалить символ, то обновить список
			Pos = ScintillaSendMessage(0, #SCI_GETCURRENTPOS) ; возвращает текущую позицию.
			WordStart = ScintillaSendMessage(0, #SCI_WORDSTARTPOSITION, Pos, 1) ; сообщения возвращают начало слова
			autocompSearchLength = Pos - WordStart
			
			*pos = ScintillaSendMessage(0, #SCI_GETCHARACTERPOINTER) ; прямой доступ
			word$ = PeekS(*pos + WordStart, autocompSearchLength, #PB_UTF8) ; #PB_ByteLength
			word$ = sep$ + word$
			PosFind = FindString(autocompList, word$, 1, #PB_String_NoCase)
			If PosFind
				Repeat
					tmp = PosFind2
					PosFind2 =FindString(autocompList, word$, PosFind2 + 1, #PB_String_NoCase)
				Until PosFind2 = 0
				If tmp
					PosFind2 = FindString(autocompList, sep$, tmp + 1)
					If PosFind2
	; 								Debug word$
	; 								Debug Str(PosFind) + " " + Str(PosFind2) + " " + Str(PosFind2 - PosFind)
	; 								если позиции не изменяются надо не перерисовывать список
						*DestinationMemoryID = AllocateMemory(PosFind2 - PosFind - 1)
						CopyMemory(*autocompList + PosFind, *DestinationMemoryID , PosFind2 - PosFind - 1)
						ScintillaSendMessage(0, #SCI_AUTOCSHOW, autocompSearchLength, *DestinationMemoryID)
						FreeMemory(*DestinationMemoryID)
					EndIf
				EndIf
	; 					Else
	; 						ScintillaSendMessage(0, #SCI_AUTOCCANCEL)
			EndIf
		EndIf
	EndWith
EndProcedure

; hdr$ = ""
; hdr$ + "----------------------------------------" + #CR$
; hdr$ + "Тестируйте автозавершение" + #CR$
; hdr$ + "----------------------------------------" + #CR$
; hdrLen = StringByteLength(hdr$, #PB_UTF8)

;- GUI
OpenWindow(0, 0, 0, 600, 700, "Тест автозавершение", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)
ScintillaGadget(0, 0, 0, WindowWidth(0), WindowHeight(0), @AutoCompletionScintillaCB())
*buffer=UTF8("Consolas")
ScintillaSendMessage(0, #SCI_STYLESETFONT, #STYLE_DEFAULT, *buffer)
FreeMemory(*buffer)
ScintillaSendMessage(0, #SCI_STYLESETSIZE, #STYLE_DEFAULT, 12) ; размер шрифта
ScintillaSendMessage(0, #SCI_SETWRAPMODE, #SC_WRAP_WHITESPACE)
ScintillaSendMessage(0, #SCI_SETCODEPAGE, #SC_CP_UTF8)
ScintillaSendMessage(0, #SCI_SETMARGINWIDTHN, 1, 0)   ; Устанавливает ширину 0, поля 1 (номеров строк)

; установить настройки CHARACTER (автодополнение, выбор и другие функции используют эти настройки)
; Задать набор символов, которые являются элементами слов
*buffer=UTF8("abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
ScintillaSendMessage(0, #SCI_SETWORDCHARS, 0, *buffer)
FreeMemory(*buffer)
;ScintillaSendMessage(0, #SCI_SETWHITESPACECHARS, 0, MakeUTF8Text(" " + #TAB$ + #CR$ + #LF$))  ;<= Uncomment this line if you want change default values
;ScintillaSendMessage(0, #SCI_SETPUNCTUATIONCHARS, 0, MakeUTF8Text("(){}[];:,'"+#DQUOTE$))     ;<= Uncomment this line if you want change default values

; *buffer=UTF8(hdr$)
; ScintillaSendMessage(0, #SCI_SETTEXT, 0, *buffer)
; FreeMemory(*buffer)
; ScintillaSendMessage(0, #SCI_GOTOPOS, hdrLen)
SetActiveGadget(0)


ScintillaSendMessage(0, #SCI_AUTOCSETMAXHEIGHT, 15) ; в символах
ScintillaSendMessage(0, #SCI_AUTOCSETMAXWIDTH, 50) ; в символах
ScintillaSendMessage(0, #SCI_AUTOCSETAUTOHIDE, #True)    ;True => автоматически скрыть, если нет совпадения
ScintillaSendMessage(0, #SCI_AUTOCSETCHOOSESINGLE, #False) ;True => автоматический выбор, если только одно совпадение
ScintillaSendMessage(0, #SCI_AUTOCSETIGNORECASE, #True)   ;True => выполнить безрегистровый поиск ('a' = 'A')
ScintillaSendMessage(0, #SCI_AUTOCSTOPS, 0, *autocompAutoCanceledByCharacters)
; ScintillaSendMessage(0, #SCI_AUTOCSETFILLUPS, 0, *autocompAutoSelectedByCharacters)
ScintillaSendMessage(0, #SCI_AUTOCSETSEPARATOR, autocompSeparatorChar)
ScintillaSendMessage(0, #SCI_AUTOCSETTYPESEPARATOR, autocompTypeChar)
; ScintillaSendMessage(0, #SCI_AUTOCSETORDER, #SC_ORDER_PERFORMSORT) ;<= потому что мой список автодополнения неупорядочен
ScintillaSendMessage(0, #SCI_AUTOCSETORDER, #SC_ORDER_PRESORTED) ;<= список отсортирован


; ScintillaSendMessage(0, #SCI_SETMODEVENTMASK, #SC_MOD_DELETETEXT) ; модификация реагирует только на удаление текста

Repeat:Until WaitWindowEvent() = #PB_Event_CloseWindow

FreeMemory(*autocompAutoCanceledByCharacters)
; FreeMemory(*autocompAutoSelectedByCharacters)
FreeMemory(*autocompList)