Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Autocomplete 1.2


  • Please log in to reply
207 replies to this topic
Pip
  • Guests
  • Last active:
  • Joined: --
Still no change...and I tried it with all three
SendMode Play
SendMode Input
SendMode Event

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
Hmmm, maybe AHK can't detect keystrokes from other scripts then...

I'm sure this is possible somehow, though.

Pip
  • Guests
  • Last active:
  • Joined: --
Yeh - hopefully someone will see this and help.

An OSK with your predictive words would be pretty awsome!

Leo Xiong
  • Members
  • 140 posts
  • Last active: Mar 15 2014 06:42 AM
  • Joined: 13 Apr 2010
Hi Uberi this is NameLess-js from #ahk at Freenode, one reason why i didn't use the 'HotKey' command to set each key is because it can't detect if the input key is visible, if you check the options in Input, you can see 'V', by definition it means

V: Visible. Normally, the user's input is hidden from the system (suppressed). Use this option to have the user's keystrokes sent to the active window.


Thanks.

Vaibhav
  • Members
  • 69 posts
  • Last active: Jul 13 2016 11:32 AM
  • Joined: 03 Dec 2010
Just completing a word is inadequate. It should help expand the phrases. Its efficiency will increase hundeds of times if it helps that way.

vaibhav

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010

Just completing a word is inadequate. It should help expand the phrases.


Did you mean autocompletion of phrases? If so, simply load up a phrase list as the word list, and remove Space from "ResetKeyList", found inside "SetHotkeys()"

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
I have the solution for OSK 8) thanks to None!!!
<!-- m -->http://www.autohotke... ... highlight=<!-- m -->

I was wondering why

:*:b::Some Text
works and suddenly I had a :idea: The mouse Clicks were resetting the Hotstring recognizer
#Hotstring NoMouse ;I have never used this B4
::Hello::This is only a test
And it worked with the OSK :shock:



kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
this works with OSK-virtual keyboard-
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
#NoEnv
#Hotstring NoMouse
WordListFile = %A_ScriptDir%\WordList.txt
MaxResults = 30
OffsetX = 0
OffsetY = 20

CoordMode, Caret
FileRead, WordList, %WordListFile%
IfInString, WordList, `r
 StringReplace, WordList, WordList, `r,, All
StringReplace, WordList, WordList, `n, `n, UseErrorLevel
MsgBox % ErrorLevel + 1
ParseList := SubStr(ParseList,1,-1), WordList .= "`n"
Gui, Add, ListBox, x0 y0 w150 h160 vMatched gCompleteWord AltSubmit
Gui, -Caption +ToolWindow +AlwaysOnTop +LastFound
hWnd := WinExist()
Gui, Show, w150 h160 Hide, AutoComplete
SetHotkeys()
Return

GuiClose:
ExitApp

SetHotkeys()
{
 NormKeyList = abcdefghijklmnopqrstuvwxyz1234567890
 ResetKeyList = Esc,Space,Home,PGUP,PGDN,End,Left,Right,RButton,MButton,Enter,`,,.,/,[,],`;,\,-,=,``
 Loop, Parse, NormKeyList
 {
  Hotkey, ~%A_LoopField%, KeyPressed, UseErrorLevel
  Hotkey, ~+%A_LoopField%, ShiftedKey, UseErrorLevel
 }
 Hotkey, ~', KeyPressed, UseErrorLevel
 Loop, Parse, ResetKeyList, CSV
  Hotkey, ~*%A_LoopField%, ResetWord, UseErrorLevel
 Hotkey, ~*", ResetWord, UseErrorLevel
}

KeyPressed:
CurrentWord .= SubStr(A_ThisHotkey,2)
Gosub, Suggest
Return

ShiftedKey:
CurrentWord .= SubStr(A_ThisHotkey,3)
Gosub, Suggest
Return

ResetWord:
CurrentWord =
Gui, Hide
Return

Suggest:
If CurrentWord =
{
 Gui, Hide
 Return
}
MatchList := "", Temp3 := StrLen(CurrentWord), Temp2 := 1
While, ((Temp1 := InStr(WordList,"," . CurrentWord,False,Temp2)) && A_Index <= MaxResults)
 Temp1 ++, Temp2 := InStr(WordList,"`n",False,Temp1), MatchList .= "|" . SubStr(WordList,Temp1,Temp2 - Temp1)
If MatchList =
{
 Gui, Hide
 Return
}
GuiControl,, Matched, %MatchList%
GuiControl, Choose, Matched, 1
Gui, Show, % "x" . (A_CaretX + OffsetX) . " y" . (A_CaretY + OffsetY) . " NoActivate"
Return

CompleteWord:
If A_GuiEvent Not In ,DoubleClick
 Return
GuiControlGet, Temp1,, Matched
MatchList .= "|"
StringGetPos, Temp1, MatchList, |, L%Temp1%
Temp1 += 2 + StrLen(CurrentWord), Temp1 := SubStr(MatchList,Temp1,InStr(MatchList,"|",False,Temp1) - Temp1)
Gosub, ResetWord
SendRaw, %Temp1%
Return

~BackSpace::
StringTrimRight, CurrentWord, CurrentWord, 1
Gosub, Suggest
Return

#IfWinExist AutoComplete ahk_class AutoHotkeyGUI

~LButton::
MouseGetPos,,, Temp1
Temp2 := WinExist("GUI Keyboard")
If Temp1 Not In %hWnd%,%Temp2%
 Gosub, ResetWord
Return

Enter::
Tab::
Gosub, CompleteWord
Return

Up::
GuiControlGet, Temp1,, Matched
GuiControl, Choose, Matched, % Temp1 - 1
Return

Down::
GuiControlGet, Temp1,, Matched
GuiControl, Choose, Matched, % Temp1 + 1
Return

#IfWinActive


berban_
  • Members
  • 202 posts
  • Last active: Aug 05 2014 11:52 PM
  • Joined: 16 Mar 2011
Pro script!

I'm a little confused on what exactly the "highly efficient method" used to scan the list is... seems to be just using InStr()?

For large amounts of data, have you considered using a binary search tree? I made an example script here: <!-- m -->http://www.autohotke...topic71683.html<!-- m --> In my example I compared the performance of an autocorrect script and it far outpaced the other method, which in my example was a parsing loop. However I just tried with InStr() and the binary search tree method was still on average 23 times faster. It will probably use more memory but will pay off in speed.

The example in the link kind of sucks so if you want to use a BST feel free to ask clarifying questions :p

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
Hi berban:

I wrote this script quite a while back (well, to me, anyways). Were I to rewrite it now, I would definitely do something along the lines of a BST, or even a hash table if I stopped caring about memory usage :D.

Maybe even a trie.

However, now that AHK_L has built in hash tables (I'm talking about Object, of course), I think it would be a pretty trivial exercise to rewrite the searching function to use it. Expect a result when my vacation ends in a few days and I fly home.

I do prefer my version for the simplicity and memory usage though, it's mainly the ListBox updates that take up all the time. There was also the idea of a virtual ListView to solve that issue as well, but I think I'm just about done here.

P.S. - Yes, the "efficient" method was InStr; other autocomplete scripts at the time of the release were searching each record one by one :shock:

berban_
  • Members
  • 202 posts
  • Last active: Aug 05 2014 11:52 PM
  • Joined: 16 Mar 2011

Were I to rewrite it now, I would definitely do something along the lines of a BST, or even a hash table ... Maybe even a trie.


I don't know what any of those things are. So I assume you won't be needing my help probably :p

...it's mainly the ListBox updates that take up all the time


I wrote an autocomplete script that used a tooltip instead of a gui to demonstrate the results, that's a fast option potentially...

I wrote this script quite a while back (well, to me, anyways).


6 months ago is quite a while back?! U beri fast learner! :D

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

However, now that AHK_L has built in hash tables

"Binary-searchable associative array" would be more accurate for current versions. It should be fast enough for most uses, and probably faster than any (completely) scripted data structure. For very large datasets it might help to use a Scripting.Dictionary object. (For smaller datasets it has a little too much overhead.)

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010

I wrote an autocomplete script that used a tooltip instead of a gui to demonstrate the results, that's a fast option potentially...


Good idea, I'll see if I can make that work with scrolling somehow.

However, now that AHK_L has built in hash tables

"Binary-searchable associative array" would be more accurate for current versions.


Really? I was under the impression that it was a hash table. Thanks for clarifying that.

berban_
  • Members
  • 202 posts
  • Last active: Aug 05 2014 11:52 PM
  • Joined: 16 Mar 2011
Here's a screenshot of the tooltip implementation:
Posted Image
As you can see, there is a cursor which you can move up and down with the arrow keys. Also, you press a number to select that entry, or press + to see the next 10 entries.
Here's the forum post: <!-- m -->http://www.autohotke...topic57577.html<!-- m -->

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010

Here's a screenshot of the tooltip implementation:
...


I eventually decided against using that method, as it did not support actuation using the mouse. Interesting concept, though.

Maybe even a trie.


Back from vacation, so here's a trie implementation:

#NoEnv

/*
WordList := "He`nHeathen`nHere`nIdentify`nIndignant`nIndividual"
Trie := TrieCreate(WordList)
Words := TrieSearch(Trie)
Serialized := TrieSerialize(Trie)
Deserialized := TrieDeserialize(Serialized,Position := 0)
SearchTrie := TrieSearch(Trie,"Ind")
SearchDeserialized := TrieSearch(Deserialized,"He")
MsgBox, WordList:`n"%WordList%"`n`nWords:`n"%Words%"`n`nSerialized:`n"%Serialized%"`n`nTrie search:`n"%SearchTrie%"`n`nDeserialized trie search:`n"%SearchDeserialized%"
*/

TrieCreate(ByRef WordList)
{
 Trie := Object("A",0,"B",0,"C",0,"D",0,"E",0,"F",0,"G",0,"H",0,"I",0,"J",0,"K",0,"L",0,"M",0,"N",0,"O",0,"P",0,"Q",0,"R",0,"S",0,"T",0,"U",0,"V",0,"W",0,"X",0,"Y",0,"Z",0,"'",0,"End",0)
 Loop, Parse, WordList, `n
 {
  CurrentObject := Trie
  Loop, Parse, A_LoopField
  {
   If !IsObject(CurrentObject[A_LoopField])
    CurrentObject[A_LoopField] := Object("A",0,"B",0,"C",0,"D",0,"E",0,"F",0,"G",0,"H",0,"I",0,"J",0,"K",0,"L",0,"M",0,"N",0,"O",0,"P",0,"Q",0,"R",0,"S",0,"T",0,"U",0,"V",0,"W",0,"X",0,"Y",0,"Z",0,"'",0,"End",0)
   CurrentObject := CurrentObject[A_LoopField]
  }
  CurrentObject.End := 1
 }
 Return, Trie
}

TrieSerialize(Trie)
{
 Serialized := ""
 For Key, Value In Trie
 {
  If (Key = "End" || !IsObject(Value))
   Continue
  Serialized .= Key . TrieSerialize(Value) . "^"
 }
 If Trie.End
  Serialized .= "~"
 Return, Serialized
}

TrieDeserialize(ByRef Serialized,ByRef Position)
{
 Trie := Object("A",0,"B",0,"C",0,"D",0,"E",0,"F",0,"G",0,"H",0,"I",0,"J",0,"K",0,"L",0,"M",0,"N",0,"O",0,"P",0,"Q",0,"R",0,"S",0,"T",0,"U",0,"V",0,"W",0,"X",0,"Y",0,"Z",0,"'",0,"End",0)
 Loop
 {
  Position ++, CurrentChar := SubStr(Serialized,Position,1)
  If (CurrentChar = "" || CurrentChar = "^")
   Return, Trie
  If (CurrentChar = "~")
   Trie.End := 1
  Else
   Trie[CurrentChar] := TrieDeserialize(Serialized,Position)
 }
}

TrieSearch(Trie,Word = "")
{
 CurrentObject := Trie, MatchList := Array(), MatchList := ""
 Loop, Parse, Word
 {
  CurrentObject := CurrentObject[A_LoopField]
  If !IsObject(CurrentObject)
   Return, 0
 }
 If CurrentObject.End
  MatchList .= Word . "`n"
 TrieDeepSearch(CurrentObject,Word,MatchList)
 Return, SubStr(MatchList,1,-1)
}

TrieDeepSearch(Trie,Word,ByRef MatchList)
{
 For Key, Value In Trie
 {
  If IsObject(Value)
  {
   If Value.End
    MatchList .= Word . Key . "`n"
   TrieDeepSearch(Value,Word . Key,MatchList)
  }
 }
}

Unfortunately, it ended up using 1.4 GB of memory just for 600000 words :shock:. That was an unworkably large amount, so I went with a prefix table (see updated first post) - kind of like a one-level trie combined with normal InStr() searching. That's how the 30x speedup was achieved. You'll find that searches never exceed 4 ms in this new version, and often fall within 0.5 ms for most use cases.

I'd like to have tried the BST, as it may have yielded even more speed benefits, but considering the math (log(6000000)/log(2) - the BST tree height), 22.5 indirections is looking a little slow.