AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Intellisense-like autoreplacement with multiple suggestions
Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9, 10  Next
 
Reply to topic    AutoHotkey Community Forum Index -> Ask for Help
View previous topic :: View next topic  
Author Message
maniac



Joined: 28 Aug 2009
Posts: 267

PostPosted: Wed Jan 20, 2010 12:20 pm    Post subject: Reply with quote

kakarukeys wrote:
2. The script can't read from wordlist.txt in Vista 64bit !?

Works for me on Win 7 64 bit... but I have UAC disabled... maybe that's why?


OK, new version. I've done 1, 2, 5, and 6 (5 is handled via the ini file) from this post, added in 2 key presses, and fixed the ini format.

Next up is 3, 4, 7, 8, hugov's up/down feature, and parameterizing the Learning capability.

Code:
;  TypingAid
;  Press 1 to 0 keys to autocomplete the word upon suggestion
;  (0 will match suggestion 10)
;                              Credits:
;                               -Jordi S
;                               -Maniac
;                               -hugov
;                               -kakarukeys
;                               -Asaptrad
;___________________________________________

; Press 1 to 0 keys to autocomplete the word upon suggestion
; (0 will match suggestion 10)
;_______________________________________

;    CONFIGURATIONS

#NoEnv
SetBatchLines, 20ms
ListLines Off

OnExit, SaveScript

;read in the preferences file
ReadPreferences()

;setup code
clearword=1

CoordMode, ToolTip, Relative
AutoTrim, Off

SetTimer, Winchanged, 100

;mark the wordlist as not done
WordListDone = 0

;reads list of words from file
FileRead, ParseWords, %A_ScriptDir%\Wordlist.txt
Loop, Parse, ParseWords, `n, `r
{
   AddWordToList(A_LoopField)
}
ParseWords =

;reverse the numbers of the word counts in memory
GoSub, ReverseWordNums

;mark the wordlist as completed
WordlistDone = 1

Loop
{
   ;Editor window check
    WinGetActiveTitle, ATitle
    WinGet, A_id, ID, %ATitle%
    IfNotInString, ATitle, %ETitle%
    {
      ToolTip
      Word=
      WinWaitActive, %ETitle%
      Continue
    }   
   
   ;Get one key at a time
   Input, chr, L1 V, %TerminatingCharacters%
   EndKey = %errorlevel%
   ; If active window has different window ID from before the input, blank word
   ; (well, assign the number pressed to the word)   
   WinGetActiveTitle, ATitle
   WinGet, A_id2, ID, %ATitle%
   IfNotEqual, A_id, %A_id2%
   {
      Gosub,clearallvars
      Word = %chr%
      Continue
   }
   
   ifequal, OldCaretY,
        OldCaretY = %A_CaretY%
   ifnotequal, OldCaretY, %A_CaretY%
   {
      ; add the word if switching lines
      AddWordToList(Word)
      Gosub,clearallvars
      Word = %chr%
      Continue         
   }

   OldCaretY=%A_CaretY%
   
      ;Backspace clears last letter
   ifequal, EndKey, Endkey:BackSpace
   {
      StringLen, len, Word
      IfNotEqual, len, 0
      {
         ifequal, len, 1   
         {
            Gosub,clearallvars
         } else {
                  StringTrimRight, Word, Word, 1
                }     
      }
   } else ifequal, EndKey, Max
         {
            if chr in %ForceNewWordCharacters%
            {
               AddWordToList(Word)
               Gosub, clearallvars
               Word = %chr%
               Continue
            } else {
                  Word .= chr
                  }
         } else {
                  AddWordToList(Word)
                  Gosub, clearallvars     
                }
   
   ;Wait till minimum letters
   IF ( StrLen(Word) < wlen )
   {
      ToolTip,
      Continue
   }
   
   ;Match part-word with command
   Num =
   Match =
   singlematch = 0
   number = 0
   StringLeft, baseword, Word, %wlen%
   baseword := ConvertWordToAscii(baseword,1)
   Loop
   {
      IfEqual, zword%baseword%%a_index%,, Break
      IfEqual, number, 10
         Break
      if ( SubStr(zword%baseword%%a_index%, 1, StrLen(Word)) = Word )
      {
         number ++
         singlematch := zword%baseword%%a_index%
         match .= Mod(number,10) . ". " . singlematch . "`n"
         singlematch%number% = %singlematch%
           
         Continue           
      }
   }
   
   ;If no match then clear Tip
   IfEqual, Match,
   {
      clearword=0
      Gosub,clearallvars
      Continue
   }
   
   ;Show matched command
   StringTrimRight, match, match, 1        ; Get rid of the last linefeed
   WinGetActiveTitle, ATitle
   WinGetPos, , PosY, , SizeY, %ATitle%
   ToolTipSizeY := (number * 12)
   ToolTipPosY := A_CaretY+14
   if ((ToolTipSizeY + ToolTipPosY) > (PosY + SizeY))
       ToolTipPosY := (A_CaretY - 14 - ToolTipSizeY)
   IfNotEqual, Word,,ToolTip, %match%, %A_CaretX%, %ToolTipPosY%
   ; +14 Move tooltip down a little so as not to hide the caret.
}

; Timed function to detect change of focus (and remove tooltip when changing active window)
Winchanged:
   WinGetActiveTitle, ATitle
   WinGet, A_id3, ID, %ATitle%
   IfNotEqual, A_id, %A_id3%
   {
      ToolTip ,
   } else {
            ; If we are in the correct window, and OldCaretY is set, clear the tooltip if not in the same line
            IfInString, ATitle, %ETitle%
            {
               IfNotEqual, OldCaretY,
               {
                  IfNotEqual, OldCaretY, %A_CaretY%   
                  {
                     ToolTip,
                  }
               }
            }
         }
   Return
   
; Key definitions for autocomplete (0 to 9)
#MaxThreadsPerHotkey 1
$1::
$2::
$3::
$4::
$5::
$6::
$7::
$8::
$9::
$0::
CheckWord(A_ThisHotkey)
Return

; If hotkey was pressed, check wether there's a match going on and send it, otherwise send the number(s) typed
CheckWord(Key)
{
   global
   Local ATitle
   Local A_id2
   Local WordIndex
   Local KeyAgain
   
   StringRight, Key, Key, 1 ;Grab just the number pushed, trim off the "$"
   
   IfEqual, Key, 0
   {
      WordIndex = 10
   } else {
            WordIndex = %Key%
         } 
   
   clearword=1
   IfEqual, NumPresses, 2
      Suspend, On

   ; If active window has different window ID from before the input, blank word
   ; (well, assign the number pressed to the word)
   WinGetActiveTitle, ATitle
   WinGet, A_id2, ID, %ATitle%
   IfNotEqual, A_id, %A_id2%
   {
      SendInput,%key%
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }
     
   IfNotEqual, OldCaretY, %A_CaretY% ;Make sure we are still on the same line
   {
      SendInput,%key%
      Gosub,clearallvars
      IfEqual, NumPresses, 2
      Suspend, Off
      Return
   }

   ifequal, Word,        ; only continue if word is not empty
   {
      SendInput,%key%
      Word = %key%
      clearword=0
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }
         
   ifequal, singlematch%WordIndex%,   ; only continue singlematch is not empty
   {
      SendInput,%key%
      Word .= key
      clearword=0
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }

   IfEqual, NumPresses, 2
   {
      Input, keyagain, L1 I T0.5, 1234567890
     
      ; If there is a timeout, abort replacement, send key and return
      IfEqual, ErrorLevel, Timeout
      {
         SendInput, %key%
         Word .= key
         clearword=0
         Gosub,clearallvars
         Suspend, off
         Return
      }

      ; Make sure it's an EndKey, otherwise abort replacement, send key and return
      IfNotInString, ErrorLevel, EndKey:
      {
         SendInput, %key%%keyagain%
         Word .= key . keyagain
         clearword=0
         Gosub, clearallvars
         Suspend, off
         Return
      }
;      ListVars
 ;     Pause
      ; If the 2nd key is NOT the same 1st trigger key, abort replacement and send keys   
      IfNotInString, ErrorLevel, %key%
      {
         StringTrimLeft, keyagain, ErrorLevel, 7
         SendInput, %key%%keyagain%
         Word .= key . keyagain
         clearword=0
         Gosub, clearallvars
         Suspend, Off
         Return
      }
   }

   Local sending
   Local len
   Local ClipboardSave
   ; SEND THE WORD!
   sending := singlematch%WordIndex%
   StringLen, len, Word
   ; Update Typed Count
   UpdateWordCount(sending)   
   SendPlay, {BS %len%}{Raw}%sending% ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt)
   ; below works but uses clipboard
   ;ClipboardSave:=ClipboardAll
   ;Clipboard = %sending%
   ;SendPlay, {BS %len%}^v ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt)
   ;Clipboard = %ClipboardSave%
   Gosub,clearallvars
   IfEqual, NumPresses, 2
      Suspend, Off
   Return
}

; This is to blank all vars related to matches, tooltip and (optionally) word
clearallvars:
      Ifequal,clearword,1
      {
         word =
         OldCaretY=
      }
      ToolTip
      ; Clear all singlematches
      Loop, 10
      {
         singlematch%a_index% =
      }
      sending =
      key=
      match=
      clearword=1
      Return

AddWordToList(AddWord)
{
   global
   Local CharTerminateList
   Local Base
   Local AddWordInList
   Local CountWord
   Local pos

   Ifequal, Addword,  ;If we have no word to add, skip out.
      Return
   if ( Substr(addword,1,1) = ";" ) ;If first char is ";", clear word and skip out.
   {
      IfEqual, wordlistdone, 0 ;If we are still reading the wordlist file and we come across ;LEARNEDWORDS; set the LearnedWordsCount flag
      {
         IfEqual, AddWord, `;LEARNEDWORDS`;
            LearnedWordsCount=0
      }
      addword =
      Return
   }
   
   ifequal, wordlistdone, 1
   {
         if addword contains 1,2,3,4,5,6,7,8,9,0,%ForceNewWordCharacters%
            Return
   }
   IF ( StrLen(addword) <= wlen ) ; don't add the word if it's not longer than the minimum length
   {
      addword =
      Return
   }

   Base := ConvertWordToAscii(SubStr(addword,1,wlen),1)
   IfEqual, WordListDone, 0 ;if this is read from the wordlist
   {
      IfNotEqual,LearnedWordsCount,  ;if this is a stored learned word
      {
         CountWord := ConvertWordToAscii(addword,0)
         IfEqual, LearnedWords,     ;if we haven't learned any words yet, set the LearnedWords list to the new word
         {
            LearnedWords = %addword% 
         } else {   ;otherwise append the learned word to the list
                  LearnedWords .= "," . addword
               }
         zCount%CountWord% := LearnedWordsCount++    ;increment the count and store the Weight of the LearnedWord in reverse order (will be inverted later)
      }
      IncrementCounterAndAddWord(Base,AddWord)
     
   } else { ; If this is an on-the-fly learned word
            AddWordInList =
            Loop ;Check to see if the word is already in the list, case sensitive
            {
               IfEqual, zword%base%%a_index%,, Break
               if ( zword%base%%a_index% == AddWord )
               {
                  AddWordInList = 1
                  Break
               }           
               Continue           
            }
           
            IfEqual, AddWordInList, ; if the word is not in the list
            {
               CountWord := ConvertWordToAscii(addWord,0)
               zCount%CountWord% = 1   ;set the count to one as it's the first time we typed it
               IfEqual, LearnedWords,    ;if we haven't learned any words yet, set the LearnedWords list to the new word
               {
                  LearnedWords = %addword% 
               } else {   ;otherwise append the learned word to the list
                        LearnedWords .= "," . addword
                     }
               IncrementCounterAndAddWord(Base,AddWord)
            } else {
                     UpdateWordCount(addword) ;Increment the word count if it's already in the list
                  }
         }
   
   Return
}

IncrementCounterAndAddWord(Base,AddWord)
{
   global
   local pos
   ; Increment the counter for each hash
   zbasenum%Base%++       
   pos := zbasenum%Base%
   ; Set the hashed value to the word
   zword%Base%%pos% = %addword%
}
   
; This sub will reverse the read numbers since now we know the total number of words
ReverseWordNums:
LearnedWordsCount+=4
Loop,parse,LearnedWords, `,
{
   AsciiWord := ConvertWordToAscii(A_LoopField,0)
   zCount%AsciiWord% := LearnedWordsCount - zCount%AsciiWord%
}

AsciiWord =
LearnedWordsCount =

Return

UpdateWordCount(word)
{
; If the Count for the word already exists - ie if it's a learned word, increment it, else don't.
   local CountWord := ConvertWordToAscii(word,0)
   IfNotEqual, zCount%CountWord%,
   {
      zCount%CountWord%++ 
      local WordBase
      StringLeft, WordBase, word, %wlen% ;find the pseudohash for the word
      WordBase := ConvertWordToAscii(WordBase,1)
      Local ConvertWord =
      Local LowIndex =
      Local WordList =
      Loop
      {
         ifequal, zword%WordBase%%A_Index%, ;Break the loop if no more words to read for the hash
            Break
         CountWord := zword%WordBase%%A_Index% ;Set CountWord to the current Word position
         ConvertWord := ConvertWordToAscii(CountWord,0) ; Find the Ascii equivalent of the word
         IfNotEqual, zCount%ConvertWord%,  ;If there's no count for this word do nothing
         {
            IfEqual, LowIndex,
               LowIndex = %A_Index% ;If this is the first word we've found with a count set this as our starting position
               
            WordList .= "," . zCount%ConvertWord% . "z" . CountWord ;prefix all words with (zCount"z")
         }
      }
     
      ifnotequal, Wordlist, ;If we have no words to process, don't
      {
         StringTrimLeft, WordList, WordList, 1
         Sort, WordList, N R D, ;Sort the wordlist by order of
         
         LowIndex-- ;A_Index starts at 1 so this value needs to be decremented
         Local IndexPos =
         Loop, Parse, WordList, `,
         {
            IndexPos := LowIndex + A_Index ;Set the current word we are processing to the starting pos plus word position
            StringTrimLeft, CountWord, A_LoopField, InStr(A_LoopField,"z") ;Strip (Number,"z") from beginning
            zword%WordBase%%IndexPos% = %CountWord% ; update the word in the list
           
         }
      }
   }
   Return
}
     
ConvertWordToAscii(Base,Caps)
{
; Return the word in Ascii numbers padded to length 3 per character
; Capitalize the string if NoCaps is not set
   IfEqual, Caps, 1
      StringUpper, Base, Base
   Loop, Parse, Base
   {
      New .= SubStr("00" . Asc(A_LoopField),-2)
   }
Return New
}

ReadPreferences()
{
   global ETitle
   global TerminatingCharacters
   global ForceNewWordCharacters
   global Wlen
   global NumPresses
   Prefs = %A_ScriptDir%\Preferences.ini
   DftTerminatingCharacters = {enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,:¿?¡!'"()]{}{}}{{}
   If FileExist(Prefs)
   {
      IniRead, ETitle, %Prefs%, Settings, Title, %A_Space%
      IniRead, TerminatingCharacters, %Prefs%, Settings, TerminatingCharacters, %DftTerminatingCharacters%
      IniRead, ForceNewWordCharacters, %Prefs%, Settings, ForceNewWordCharacters, %A_Space%
      IniRead, Wlen, %Prefs%, Settings, Length, 3
      IniRead, NumPresses, %Prefs%, Settings, NumPresses, 1
   } else {
            INI=
               (
;Title is a string of text to find in the title of the window you want TypingAid enabled for. If you leave
;it blank it will work in all windows.
[Settings]
Title=
;
;TerminatingCharacters is a list of EndKey characters which will signal the script that you are done typing a word.
;A list of keys may be found here:
; http://www.autohotkey.com/docs/KeyList.htm
;Certain characters (such as `% or , ) require an escape character in front, see here:
; http://www.autohotkey.com/docs/commands/_EscapeChar.htm
;For more detail on how to format the list of characters please see the EndKeys section (paragraphs 2,3,4) of:
; http://www.autohotkey.com/docs/commands/Input.htm
TerminatingCharacters={enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,:¿?¡!'"()]{}{}}{{}
;
;ForceNewWordCharacters is a comma separated list of characters which force the script to start a new word whenever
;one of those characters is typed. Any words which begin with one of these characters will never be learned (even
;if learning is enabled). If you were typing a word when you hit one of these characters that word will be learned
;if learning is enabled.
ForceNewWordCharacters=
;
;Length is the minimum number of characters required to show the list of words. The higher this number the better performance will be.
Length=3
;
;NumPresses is the number of times the hotkey must be pushed for the word to be selected, either 1 or 2.
NumPresses=1
               )               
               FileAppend, %INI%, %A_ScriptDir%\Preferences.ini
         }
   

   IfEqual, Wlen,
      Wlen = 3
   
   IfEqual, TerminatingCharacters,
      TerminatingCharacters = DftTerminatingCharacters
     
   IfEqual, NumPresses,
      NumPresses = 1
     
   Return
}
   
SaveScript:
; Close the tooltip if it's open
ToolTip,
; Add all the standard words to the tempwordlist
FileRead, ParseWords, %A_ScriptDir%\Wordlist.txt
LearnedwordsPos := InStr(ParseWords, "`;LEARNEDWORDS`;",true,1) ;Check for Learned Words
IfNotEqual, LearnedwordsPos, 0
{
   TempWordList := SubStr(ParseWords, 1, LearnedwordsPos - 1) ;Grab all non-learned words out of list
} else {
         TempWordList := ParseWords
      }
ParseWords =
; Parse the learned words and store them in a new list by count if their total count is greater than 5.
; Prefix the word with the count and "z" for sorting
Loop, Parse, LearnedWords, `,
{
   SortWord := ConvertWordToAscii(A_LoopField,0)
   
   IfGreaterOrEqual, zCount%SortWord%, 5
   {
      SortWordList .= "," . zCount%SortWord% . "z" . A_LoopField
   }
}

StringTrimLeft, SortWordList, SortWordList, 1 ;remove extra starting comma

Sort, SortWordList, N R D, ; Sort numerically, comma delimiter

IfNotEqual, SortWordList, ; If SortWordList exists write to the file, otherwise don't.
{
   TempWordList .= "`;LEARNEDWORDS`;`r`n"
   Loop, Parse, SortWordList, `,
   {
      StringTrimLeft, AppendWord, A_LoopField, InStr(A_LoopField,"z") ;Strip (Number,"z") from beginning
      TempWordList .= AppendWord . "`r`n"
   }
   StringTrimRight, TempWordList, TempWordList, 2
   
   FileDelete, %A_ScriptDir%\Temp_Wordlist.txt
   FileAppend, %TempWordList%, %A_ScriptDir%\Temp_Wordlist.txt ;Only update the file if we have learned words
   FileCopy, %A_ScriptDir%\Temp_Wordlist.txt, %A_ScriptDir%\Wordlist.txt, 1
   FileDelete, %A_ScriptDir%\Temp_Wordlist.txt
}

ExitApp
Back to top
View user's profile Send private message
Guest






PostPosted: Wed Jan 20, 2010 1:43 pm    Post subject: Reply with quote

Quote:
I'm quite sure (without looking) you have requested the same up/down feature in similar threads. But we'll never know for sure because of the guest ID, why not choose a nick makes it so much easier to talk to someone Smile And its easy to try yourself if it has the new feature by simply trying, why ask all the time.


I ONLY posted in this thread. You better believe it.
I don't have to try all your modifications 'coz I'm happy with the old one.
It works for me.
I just need that arrow feature.
Don't need to be trying everything new that comes along.
My nick, couldn't be better.
Back to top
SoLong&Thx4AllTheFish



Joined: 27 May 2007
Posts: 4999

PostPosted: Wed Jan 20, 2010 1:51 pm    Post subject: Reply with quote

Anonymous wrote:
a) I don't have to try all your modifications 'coz I'm happy with the old one.
b) It works for me.
c) I just need that arrow feature.

a) they aren't my mods
b) good for you
c) I don't think it will happen any time soon, it will take some sort of genius to do that
_________________
AHK Wiki FAQ
TF : Text files & strings lib, TF Forum
Back to top
View user's profile Send private message
kakarukeys



Joined: 28 Sep 2009
Posts: 86

PostPosted: Thu Jan 21, 2010 3:44 am    Post subject: Reply with quote

hugov wrote:
Just a quick hack to add tab + enter completion while tooltip is active, will either complete first or use the selected item in the list, no guis where harmed in the process Smile so you can use the vertical arrow keys on the keyboard now.


I'm abit concerned by the impact on usability. The user now cannot type a newline when the tooltip is active. This reminds me of the autocompletion in MS Word which I quickly turned off when I found it was active. In MS Word autocompletion, Enter key is used to autocomplete a word, that gets in the user's way if what the user is trying to do is to type a newline. May I suggest not to use Enter key or make it an option in the preference file?

The worse case scenario: to type 'config\n' when a word 'configuration' is in the wordlist.

Other than this, I think the implemention is perfect and suits the guest's request.

maniac wrote:
kakarukeys wrote:
2. The script can't read from wordlist.txt in Vista 64bit !?

Works for me on Win 7 64 bit... but I have UAC disabled... maybe that's why?


Is it due to a protection mechanism from UAC? when you have UAC enabled, will it not work?
Sorry I don't have 64bits OS here.

Anonymous wrote:
I'm not asking in various threads. I'm asking in the SAME thread. And no, that doesn't answer my question. And so, I'm gonna ask again in the future.


Anonymous wrote:
Quote:
I'm quite sure (without looking) you have requested the same up/down feature in similar threads. But we'll never know for sure because of the guest ID, why not choose a nick makes it so much easier to talk to someone Smile And its easy to try yourself if it has the new feature by simply trying, why ask all the time.


I ONLY posted in this thread. You better believe it.
I don't have to try all your modifications 'coz I'm happy with the old one.
It works for me.
I just need that arrow feature.
Don't need to be trying everything new that comes along.
My nick, couldn't be better.


Hi,
I think the new feature by hugov has the arrow thing you want. Someone did reply your question. It meant in future he may put in the arrow feature when he feels the time is right. This also reflects the reality when a software is being developed for free / open-source. It's a project that we do when we have the motivation, curiosity, desire to help, etc, not monetary gain. We have a handful of requests, we are not working full-time so we can't possibly satisfy everyone at one go. A rule of thumb is that we fix things that are broken first, then implement optional feature. In this case, the arrow feature is optional because 1-9,0 keys are already being used to select words.

I hope this will sort things out. You are welcome to post bug report, feature request, and usually once is enough.
_________________
TypingAid autocompletion program made with AHK.
Back to top
View user's profile Send private message
SoLong&Thx4AllTheFish



Joined: 27 May 2007
Posts: 4999

PostPosted: Thu Jan 21, 2010 8:12 am    Post subject: Reply with quote

Future feature suggestions:

- A toggle hotkey to activate/suspend script, also via tray menu (configurable)
- Add automatic space after auto-completion (configurable)
- Also introduce exclude programs, e.g. work everywhere except in notepad
_________________
AHK Wiki FAQ
TF : Text files & strings lib, TF Forum
Back to top
View user's profile Send private message
maniac



Joined: 28 Aug 2009
Posts: 267

PostPosted: Thu Jan 21, 2010 10:10 am    Post subject: Reply with quote

hugov wrote:
Future feature suggestions:

- A toggle hotkey to activate/suspend script, also via tray menu (configurable)
- Add automatic space after auto-completion (configurable)
- Also introduce exclude programs, e.g. work everywhere except in notepad


Good ideas, thanks... I'll keep them in mind.

kakarukeys wrote:
I'm abit concerned by the impact on usability. The user now cannot type a newline when the tooltip is active. This reminds me of the autocompletion in MS Word which I quickly turned off when I found it was active. In MS Word autocompletion, Enter key is used to autocomplete a word, that gets in the user's way if what the user is trying to do is to type a newline. May I suggest not to use Enter key or make it an option in the preference file?

The worse case scenario: to type 'config\n' when a word 'configuration' is in the wordlist.

Other than this, I think the implemention is perfect and suits the guest's request.


I think that's how most of these types of autocompletion utilities work when they have an up/down feature. Maybe ctrl-enter would be better though.

I plan on making the Up/Down able to be disabled/enabled via the preferences file.
Back to top
View user's profile Send private message
maniac



Joined: 28 Aug 2009
Posts: 267

PostPosted: Thu Jan 21, 2010 10:20 am    Post subject: Reply with quote

kakarukeys wrote:
maniac wrote:
kakarukeys wrote:
2. The script can't read from wordlist.txt in Vista 64bit !?

Works for me on Win 7 64 bit... but I have UAC disabled... maybe that's why?


Is it due to a protection mechanism from UAC? when you have UAC enabled, will it not work?
Sorry I don't have 64bits OS here.

Maybe? I don't know... I don't want to enable UAC to find out Wink... also UAC shouldn't matter between 64 and 32 bit OS's as both have UAC.
Back to top
View user's profile Send private message
maniac



Joined: 28 Aug 2009
Posts: 267

PostPosted: Thu Jan 21, 2010 3:12 pm    Post subject: Reply with quote

Phew, that's everything except for the new stuff hugov suggested (I think those should wait for a new version).

Up/Down arrow feature works, with 4 different methods (controlled via preferences):
Off, turns it off totally
LastWord, keeps the selected word if still in the list, otherwise reverts to the first item.
LastPosition, keeps the current selected position (if the new total number is less than the last selected position it changes to the new total number)
First, always selects the first item in the list when you type a new character.

Thanks for the Up/Down ideas hugov!

The Ctrl-Shift-C feature works, but it behaves a little differently depending on the learning mode.
LearnMode = On: Word is permanently learned by giving it a count of 5 (if not already higher). It will be ranked and stored like other Learned words.
LearnMode = Off: Word is simply tagged onto the end of the list.

I added in all of kakarukeys changes except for the splash screen, it wasn't really generic enough to even add in as a parameter.

Please let me know if you find any issues or have any suggestions, if not we can make a thread over in the scripts section and get the official release out.

One thing about the terminating characters:
default is:
{enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,¿?¡!'"()]{}{}}{{}:
I think it should be:
{enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,¿?¡!'"()[]{}{}}{{}:~```%$&*-+=\/><^|@#

Is that fine?

Also, what about underscore _ ?

Kakarukeys, I think you should release specialized preferences files to deal with non-english languages.

Code:
;  TypingAid
;  Press 1 to 0 keys to autocomplete the word upon suggestion
;  (0 will match suggestion 10)
;                              Credits:
;                               -Jordi S
;                               -Maniac
;                               -hugov
;                               -kakarukeys
;                               -Asaptrad
;___________________________________________

; Press 1 to 0 keys to autocomplete the word upon suggestion
;___________________________________________

;    CONFIGURATIONS

#NoEnv
SetBatchLines, 20ms
ListLines Off
OnExit, SaveScript

;read in the preferences file
ReadPreferences()

;setup code
clearword=1
MouseX = 0
MouseY = 0
Helper_id =
CoordMode, Mouse, Relative
CoordMode, ToolTip, Relative
AutoTrim, Off

SetTimer, Winchanged, 100

;mark the wordlist as not done
WordListDone = 0

;reads list of words from file
FileRead, ParseWords, %A_ScriptDir%\Wordlist.txt
Loop, Parse, ParseWords, `n, `r
{
   AddWordToList(A_LoopField,0)
}
ParseWords =

;reverse the numbers of the word counts in memory
GoSub, ReverseWordNums

;mark the wordlist as completed
WordlistDone = 1

Loop
{
   ;Editor window check
    WinGetActiveTitle, ATitle
    WinGet, A_id, ID, %ATitle%
    IfNotInString, ATitle, %ETitle%
    {
      ToolTip
      Word=
      WinWaitActive, %ETitle%
      ATitle =
      Continue
   }   
   ATitle =
   
   ;Get one key at a time
   Input, chr, L1 V, %TerminatingCharacters%
   EndKey = %errorlevel%
   ; If active window has different window ID from before the input, blank word
   ; (well, assign the number pressed to the word)   
   WinGet, A_id2, ID, A
   IfNotEqual, A_id, %A_id2%
   {
      Gosub,clearallvars
      Word = %chr%
      Continue
   }
   
   ifequal, OldCaretY,
        OldCaretY := HCaretY()
   if ( OldCaretY != HCaretY() )
   {
      ; add the word if switching lines
      AddWordToList(Word,0)
      Gosub,clearallvars
      Word = %chr%
      Continue         
   }

   OldCaretY := HCaretY()
   
      ;Backspace clears last letter
   ifequal, EndKey, Endkey:BackSpace
   {
      StringLen, len, Word
      IfNotEqual, len, 0
      {
         ifequal, len, 1   
         {
            Gosub,clearallvars
         } else {
                  StringTrimRight, Word, Word, 1
                }     
      }
   } else ifequal, EndKey, Max
         {
            if chr in %ForceNewWordCharacters%
            {
               AddWordToList(Word,0)
               Gosub, clearallvars
               Word = %chr%
               Continue
            } else {
                  Word .= chr
                  }
         } else {
                  AddWordToList(Word,0)
                  Gosub, clearallvars     
                }
   
   ;Wait till minimum letters
   IF ( StrLen(Word) < wlen )
   {
      ToolTip,
      Continue
   }

   IfNotEqual, MatchPos,
   {
      IfEqual, ArrowKeyMethod, LastWord
      {
         OldMatch := singlematch%MatchPos%
      } else {
               IfEqual, ArrowKeyMethod, LastPosition
               {
                  OldMatch = %MatchPos%
               } else {
                        OldMatch =
                     }
            }
   
   } else {
            OldMatch =
         }

   ;Match part-word with command
   Num =
   number = 0
   StringLeft, baseword, Word, %wlen%
   baseword := ConvertWordToAscii(baseword,1)
   Loop
   {
      IfEqual, zword%baseword%%a_index%,, Break
      IfEqual, number, 10
         Break
      if ( SubStr(zword%baseword%%a_index%, 1, StrLen(Word)) = Word )
      {
         number ++
         singlematch := zword%baseword%%a_index%
         singlematch%number% = %singlematch%
           
         Continue           
      }
   }
   
   ;If no match then clear Tip
   IfEqual, number, 0
   {
      clearword=0
      Gosub,clearallvars
      Continue
   }

   IfEqual, OldMatch,
   {
      IfEqual, ArrowKeyMethod, Off
      {
         MatchPos =
      } else {
               MatchPos = 1
            }
   } Else {
            IfEqual, ArrowKeyMethod, Off
            {
               MatchPos =
            } else {
                     IfEqual, ArrowKeyMethod, LastPosition
                     {
                        IfGreater, OldMatch, %Number%
                        {
                           MatchPos = %Number%
                        } else {
                                 MatchPos = %OldMatch%
                              }
                     
                     } else {
                              IfEqual, ArrowKeyMethod, LastWord
                              {
                                 Pos =
                                 Loop, %Number%
                                 {
                                    if ( OldMatch == singlematch%A_Index% )
                                    {
                                    Pos = %A_Index%
                                    Break
                                    }
                                 }
                                 IfEqual, pos,
                                 {
                                    MatchPos = 1
                                 } Else {
                                          MatchPos = %Pos%
                                       }
                              } else {
                                       MatchPos = 1
                                    }
                                 
                           }
                  }
         }   
   OldMatch =
   RebuildMatchList()
   ShowToolTip()
}

RebuildMatchList()
{
   global
   match =
   Loop, %Number%
   {
      AddToMatchList(A_Index,singlematch%A_Index%)
   }
   StringTrimRight, match, match, 1        ; Get rid of the last linefeed
   Return
}

AddToMatchList(position,value)
{
   global
   IfEqual, ArrowKeyMethod, Off
   {
      match .= Mod(position,10) . ". " . value . "`n"
   } else {
            IfEqual, MatchPos, %Position%
            {
               match .= ">" . Mod(position,10) . ". " . value . "`n"
            } Else {
                     match .= "   " . Mod(position,10) . ". " . value . "`n"
                  }
         }
}

;Show matched values
ShowToolTip()
{
   global ToolTipOffset
   global Word
   global Match
   WinGetPos, , PosY, , SizeY, A
   ToolTipSizeY := (number * 12)
   ToolTipPosY := HCaretY()+ToolTipOffset
   ; + ToolTipOffset Move tooltip down a little so as not to hide the caret.
   if ((ToolTipSizeY + ToolTipPosY) > (PosY + SizeY))
       ToolTipPosY := (HCaretY() - ToolTipOffset - ToolTipSizeY)
   IfNotEqual, Word,
      ToolTip, %match%, HCaretX(), %ToolTipPosY%
}
   

; Timed function to detect change of focus (and remove tooltip when changing active window)
Winchanged:
   WinGetActiveTitle, ATitle
   WinGet, A_id3, ID, %ATitle%
   IfNotEqual, A_id, %A_id3%
   {
      ToolTip ,
   } else {
            ; If we are in the correct window, and OldCaretY is set, clear the tooltip if not in the same line
            IfInString, ATitle, %ETitle%
            {
               IfNotEqual, OldCaretY,
               {
                  if ( OldCaretY != HCaretY() )
                  {
                     ToolTip,
                  }
               }
            }
         }
   ATitle =
   A_Id3 =
   Return


; Update last click position in case Caret is not detectable
~LButton::
MouseGetPos, MouseX, MouseY
Return
   
; Key definitions for autocomplete (0 to 9)
#MaxThreadsPerHotkey 1
$1::
$2::
$3::
$4::
$5::
$6::
$7::
$8::
$9::
$0::
CheckWord(A_ThisHotkey)
Return

$^Enter::
$^Space::
$Tab::
$Up::
$Down::
$PgUp::
$PgDn::
EvaluateUpDown(A_ThisHotKey)
Return

^+h::
CreateHelperWindow()
Return

^+c::
AddSelectedWordToList()
Return

; If hotkey was pressed, check wether there's a match going on and send it, otherwise send the number(s) typed
CheckWord(Key)
{
   global
   Local ATitle
   Local A_id2
   Local WordIndex
   Local KeyAgain
   
   StringRight, Key, Key, 1 ;Grab just the number pushed, trim off the "$"
   
   IfEqual, Key, 0
   {
      WordIndex = 10
   } else {
            WordIndex = %Key%
         } 
   
   clearword=1
   IfEqual, NumPresses, 2
      Suspend, On

   ; If active window has different window ID from before the input, blank word
   ; (well, assign the number pressed to the word)
   if ( ReturnWinActive() = )
   {
      SendInput,%key%
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }
   
   if ReturnLineWrong() ;Make sure we are still on the same line
   {
      SendInput,%key%
      Gosub,clearallvars
      IfEqual, NumPresses, 2
      Suspend, Off
      Return
   }

   ifequal, Word,        ; only continue if word is not empty
   {
      SendInput,%key%
      Word = %key%
      clearword=0
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }
         
   ifequal, singlematch%WordIndex%,   ; only continue singlematch is not empty
   {
      SendInput,%key%
      Word .= key
      clearword=0
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }

   IfEqual, NumPresses, 2
   {
      Input, keyagain, L1 I T0.5, 1234567890
     
      ; If there is a timeout, abort replacement, send key and return
      IfEqual, ErrorLevel, Timeout
      {
         SendInput, %key%
         Word .= key
         clearword=0
         Gosub,clearallvars
         Suspend, off
         Return
      }

      ; Make sure it's an EndKey, otherwise abort replacement, send key and return
      IfNotInString, ErrorLevel, EndKey:
      {
         SendInput, %key%%keyagain%
         Word .= key . keyagain
         clearword=0
         Gosub, clearallvars
         Suspend, off
         Return
      }
   
      ; If the 2nd key is NOT the same 1st trigger key, abort replacement and send keys   
      IfNotInString, ErrorLevel, %key%
      {
         StringTrimLeft, keyagain, ErrorLevel, 7
         SendInput, %key%%keyagain%
         Word .= key . keyagain
         clearword=0
         Gosub, clearallvars
         Suspend, Off
         Return
      }
   }

   SendWord(WordIndex)
   IfEqual, NumPresses, 2
      Suspend, Off
   Return
}

SendWord(WordIndex)
{
   global
   Local sending
   Local len
   ;Local ClipboardSave
   ;Send the word
   sending := singlematch%WordIndex%
   StringLen, len, Word
   ; Update Typed Count
   UpdateWordCount(sending,0)
   SendPlay, {BS %len%}{Raw}%sending% ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt)
   ; below works but uses clipboard
   ;ClipboardSave:=ClipboardAll
   ;Clipboard = %sending%
   ;SendPlay, {BS %len%}^v ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt)
   ;Clipboard = %ClipboardSave%
   Gosub clearallvars
   Return


;If a hotkey related to the up/down arrows was pressed
EvaluateUpDown(Key)
{
   global
   
   IfEqual, ArrowKeyMethod, Off
   {
      SendKey(Key)
      Return
   }
   
   IfEqual, Match,
   {
      SendKey(Key)
      Return
   }

   if ( ReturnWinActive() = )
   {
      SendKey(Key)
      Gosub, ClearAllVars
      Return
   }

   if ReturnLineWrong()
   {
      SendKey(Key)
      GoSub, ClearAllVars
      Return
   }   
   
   IfEqual, Word, ; only continue if word is not empty
   {
      SendKey(Key)
      ClearWord = 0
      GoSub, ClearAllVars
      Return
   }
   
   if ( ( Key = "$^Enter" ) || ( Key = "$Tab" ) || ( Key = "$^Space" ) )
   {
     
      IfEqual, singlematch%MatchPos%, ;only continue if singlematch is not empty
      {
         SendKey(Key)
         MatchPos = %Number%
         RebuildMatchList()
         ShowToolTip()
         Return
      }
     
      SendWord(MatchPos)
      Return
     
   }
   IfEqual, Key, $Up
   {   
      MatchPos--
   } else {
            IfEqual, Key, $Down
            {
               MatchPos++
            } else {
                     IfEqual, Key, $PgUp
                     {
                        MatchPos = 1
                     } else {
                              IfEqual, Key, $PgDn
                              {
                                 MatchPos = Number
                              }
                           }
                  }
         }
   IfLess, MatchPos, 1
   {
      MatchPos = 1
   } else {
            IfGreater, MatchPos, %Number%
            {
               MatchPos = %Number%
            }
         }
   RebuildMatchList()
   ShowToolTip()
   Return
}
           
SendKey(Key)
{
   IfEqual, Key, $^Enter
   {
      Key = ^{Enter}
   } else {
            IfEqual, Key, $^Space
            {
               Key = ^{Space}
            } else {
                     Key := "{" . SubStr(Key, 2) . "}"
                  }
         }
     
   SendInput, %Key%
   Return
}
   
     
     
ReturnWinActive()
{
   global A_id
   WinGet, A_id2, ID, A
   Return, ( A_id = A_id2 )
}

ReturnLineWrong()
{
   global OldCaretY
   Return, ( OldCaretY != HCaretY() )
}

;Create helper window for showing tooltip
CreateHelperWindow()
{
   Global Helper_id
   Gui, Add, Text,,Tooltip appears here
   Gui, Show
   WinGet, Helper_id, ID,,Tooltip appears here
   WinSet, AlwaysOnTop, On, ahk_id %Helper_id%
   return
}

; function to grab the X position of the caret for the tooltip
HCaretX()
{
    global MouseX
    global Helper_id
   
    WinGetPos, HelperX,,,, ahk_id %Helper_id%
    WinGetPos, X,,,, A
   
    if HelperX !=
    {
        if X !=
        {
            return HelperX - X
        }
    }
    if A_CaretX < 14
    {
        if MouseX != 0
        {
            return MouseX
        }
    }
    return A_CaretX
}

; function to grab the Y position of the caret for the tooltip
HCaretY()
{
    global MouseY
    global Helper_id
   
    WinGetPos,,HelperY,,, ahk_id %Helper_id%
    WinGetPos,, Y,,, A
    if HelperY !=
    {
        if Y !=
        {
            return HelperY - Y
        }
    }
    if A_CaretX < 14
    {
        if MouseY != 0
        {
            return MouseY + 20
        }
    }
    return A_CaretY
}

AddSelectedWordToList()
{
   ClipboardSave := ClipboardAll
   Clipboard =
   Sleep, 100
   SendInput, ^c
   ClipWait, 0
   IfNotEqual, Clipboard,
   {
      AddWordToList(Clipboard,1)
   }
   Clipboard = %ClipboardSave%
}

AddWordToList(AddWord,ForceCountNewOnly)
{
   ;AddWord = Word to add to the list
   ;ForceCountNewOnly = force this word to be permanently learned even if learnmode is off
   global
   Local CharTerminateList
   Local Base
   Local AddWordInList
   Local CountWord
   Local pos
   Local LearnModeTemp
   
   IfEqual, LearnMode, On
   {
      LearnModeTemp = 1
   } else {
            IfEqual, ForceCountNewOnly, 1
               LearnModeTemp = 1
         }

   Ifequal, Addword,  ;If we have no word to add, skip out.
      Return
   if ( Substr(addword,1,1) = ";" ) ;If first char is ";", clear word and skip out.
   {
      IfEqual, LearnMode, On ;Check LearnMode here as we only do this if the wordlist is not done
      {
         IfEqual, wordlistdone, 0 ;If we are still reading the wordlist file and we come across ;LEARNEDWORDS; set the LearnedWordsCount flag
         {
            IfEqual, AddWord, `;LEARNEDWORDS`;
               LearnedWordsCount=0
         }
      }
      Return
   }
   IF ( StrLen(addword) <= wlen ) ; don't add the word if it's not longer than the minimum length
   {
      Return
   }
   
   ifequal, wordlistdone, 1
   {
      IfNotEqual, LearnModeTemp, 1
      {
         Return   
      } else {
               if addword contains 1,2,3,4,5,6,7,8,9,0,%ForceNewWordCharacters%
                  Return
            }
   }

   Base := ConvertWordToAscii(SubStr(addword,1,wlen),1)
   IfEqual, WordListDone, 0 ;if this is read from the wordlist
   {
      IfNotEqual,LearnedWordsCount,  ;if this is a stored learned word
      {
         CountWord := ConvertWordToAscii(addword,0)
         IfEqual, LearnedWords,     ;if we haven't learned any words yet, set the LearnedWords list to the new word
         {
            LearnedWords = %addword% 
         } else {   ;otherwise append the learned word to the list
                  LearnedWords .= "," . addword
               }
         zCount%CountWord% := LearnedWordsCount++    ;increment the count and store the Weight of the LearnedWord in reverse order (will be inverted later)
      }
      IncrementCounterAndAddWord(Base,AddWord)
     
   } else { ; If this is an on-the-fly learned word
            AddWordInList =
            Loop ;Check to see if the word is already in the list, case sensitive
            {
               IfEqual, zword%base%%a_index%,, Break
               if ( zword%base%%a_index% == AddWord )
               {
                  AddWordInList = 1
                  Break
               }           
               Continue           
            }
           
            IfEqual, AddWordInList, ; if the word is not in the list
            {
               CountWord := ConvertWordToAscii(addWord,0)
               IfEqual, ForceCountNewOnly, 1
               {
                  zCount%CountWord% = 5 ;set the count to 5 so it gets written to the file
               } else {
                        zCount%CountWord% = 1   ;set the count to one as it's the first time we typed it
                     }
               IfEqual, LearnedWords,    ;if we haven't learned any words yet, set the LearnedWords list to the new word
               {
                  LearnedWords = %addword% 
               } else {   ;otherwise append the learned word to the list
                        LearnedWords .= "," . addword
                     }
               IncrementCounterAndAddWord(Base,AddWord)
               
               IfEqual, LearnMode, On
               {
                  IfEqual, ForceCountNewOnly, 1
                     UpdateWordCount(addword,1) ;resort the necessary words if it's a forced added word
               }
            } else {
                     IfEqual, ForceCountNewOnly, 1
                     {
                        CountWord := ConvertWordToAscii(addWord,0)
                        IF ( zCount%CountWord% < 5 )
                           zCount%CountWord% = 5
                        UpdateWordCount(addWord,1)Icon
                     } else {
                              IfEqual, LearnMode, On
                              {
                                 UpdateWordCount(addword,0) ;Increment the word count if it's already in the list and we aren't forcing it on
                              }
                           }
                  }
         }
   
   Return
}

IncrementCounterAndAddWord(Base,AddWord)
{
   global
   local pos
   ; Increment the counter for each hash
   zbasenum%Base%++       
   pos := zbasenum%Base%
   ; Set the hashed value to the word
   zword%Base%%pos% = %addword%
}
   
; This sub will reverse the read numbers since now we know the total number of words
ReverseWordNums:
LearnedWordsCount+=4
Loop,parse,LearnedWords, `,
{
   AsciiWord := ConvertWordToAscii(A_LoopField,0)
   zCount%AsciiWord% := LearnedWordsCount - zCount%AsciiWord%
}

AsciiWord =
LearnedWordsCount =

Return

UpdateWordCount(word,SortOnly)
{
   ;Word = Word to increment count for
   ;SortOnly = Only sort the words, don't increment the count
   ; If the Count for the word already exists - ie if it's a learned word, increment it, else don't.
   local CountWord := ConvertWordToAscii(word,0)
   IfNotEqual, zCount%CountWord%,
   {
      IfNotEqual, SortOnly, 1 ;don't increment the count if we only want to sort the words
         zCount%CountWord%++ 
      local WordBase
      StringLeft, WordBase, word, %wlen% ;find the pseudohash for the word
      WordBase := ConvertWordToAscii(WordBase,1)
      Local ConvertWord =
      Local LowIndex =
      Local WordList =
      Loop
      {
         ifequal, zword%WordBase%%A_Index%, ;Break the loop if no more words to read for the hash
            Break
         CountWord := zword%WordBase%%A_Index% ;Set CountWord to the current Word position
         ConvertWord := ConvertWordToAscii(CountWord,0) ; Find the Ascii equivalent of the word
         IfNotEqual, zCount%ConvertWord%,  ;If there's no count for this word do nothing
         {
            IfEqual, LowIndex,
               LowIndex = %A_Index% ;If this is the first word we've found with a count set this as our starting position
               
            WordList .= "," . zCount%ConvertWord% . "z" . CountWord ;prefix all words with (zCount"z")
         }
      }
     
      ifnotequal, Wordlist, ;If we have no words to process, don't
      {
         StringTrimLeft, WordList, WordList, 1
         Sort, WordList, N R D, ;Sort the wordlist by order of
         
         LowIndex-- ;A_Index starts at 1 so this value needs to be decremented
         Local IndexPos =
         Loop, Parse, WordList, `,
         {
            IndexPos := LowIndex + A_Index ;Set the current word we are processing to the starting pos plus word position
            StringTrimLeft, CountWord, A_LoopField, InStr(A_LoopField,"z") ;Strip (Number,"z") from beginning
            zword%WordBase%%IndexPos% = %CountWord% ; update the word in the list
           
         }
      }
   }
   Return
}
     
ConvertWordToAscii(Base,Caps)
{
; Return the word in Ascii numbers padded to length 3 per character
; Capitalize the string if NoCaps is not set
   IfEqual, Caps, 1
      StringUpper, Base, Base
   Loop, Parse, Base
   {
      New .= SubStr("00" . Asc(A_LoopField),-2)
   }
Return New
}

; This is to blank all vars related to matches, tooltip and (optionally) word
clearallvars:
      Ifequal,clearword,1
      {
         word =
         OldCaretY=
      }
      ToolTip
      ; Clear all singlematches
      Loop, 10
      {
         singlematch%a_index% =
      }
      sending =
      key=
      match=
      MatchPos=
      clearword=1
      Return

ReadPreferences()
{
   global ETitle
   global TerminatingCharacters
   global ForceNewWordCharacters
   global Wlen
   global NumPresses
   global LearnMode
   global ToolTipOffset
   global ArrowKeyMethod
   
   Prefs = %A_ScriptDir%\Preferences.ini
   DftTerminatingCharacters = {enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,:¿?¡!'"()]{}{}}{{}
   If FileExist(Prefs)
   {
      IniRead, ETitle, %Prefs%, Settings, Title, %A_Space%
      IniRead, TerminatingCharacters, %Prefs%, Settings, TerminatingCharacters, %DftTerminatingCharacters%
      IniRead, ForceNewWordCharacters, %Prefs%, Settings, ForceNewWordCharacters, %A_Space%
      IniRead, Wlen, %Prefs%, Settings, Length, 3
      IniRead, NumPresses, %Prefs%, Settings, NumPresses, 1
      IniRead, LearnMode, %Prefs%, Settings, LearnMode, Off
      IniRead, ToolTipOffset, %Prefs%, Settings, ToolTipOffset, 14
      IniRead, ArrowKeyMethod, %Prefs%, Settings, ArrowKeyMethod, First
   } else {
            INI=
               (
;Title is a string of text to find in the title of the window you want TypingAid enabled for. If you leave
;it blank it will work in all windows.
[Settings]
Title=
;
;TerminatingCharacters is a list of EndKey characters which will signal the script that you are done typing a word.
;A list of keys may be found here:
; http://www.autohotkey.com/docs/KeyList.htm
;Certain characters (such as `% or , ) require an escape character in front, see here:
; http://www.autohotkey.com/docs/commands/_EscapeChar.htm
;For more detail on how to format the list of characters please see the EndKeys section (paragraphs 2,3,4) of:
; http://www.autohotkey.com/docs/commands/Input.htm
TerminatingCharacters={enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,:¿?¡!'"()]{}{}}{{}
;
;ForceNewWordCharacters is a comma separated list of characters which force the script to start a new word whenever
;one of those characters is typed. Any words which begin with one of these characters will never be learned (even
;if learning is enabled). If you were typing a word when you hit one of these characters that word will be learned
;if learning is enabled.
ForceNewWordCharacters=
;
;Length is the minimum number of characters required to show the list of words. The higher this number the better performance will be.
Length=3
;
;NumPresses is the number of times the hotkey must be pushed for the word to be selected, either 1 or 2.
NumPresses=1
;
;LearnMode defines whether or not the script should learn new words as you type them, either On or Off
LearnMode=On
;
;ToolTipOffset is the number of pixels below the top of the Caret (Cursor) to display the tooltip
ToolTipOffset=14
;
;ArrowKeyMethod is the way the arrow keys are handled in the drop down. Off means you can only use the number keys
;First resets the selected word to the beginning whenever you type a new character
;LastWord keeps the last word selected if still in the last, else resets to the beginning
;LastPosition keeps the last cursor position
ArrowKeyMethod=First
               )               
               FileAppend, %INI%, %A_ScriptDir%\Preferences.ini
         }
   

   IfEqual, Wlen,
      Wlen = 3
   
   IfEqual, TerminatingCharacters,
      TerminatingCharacters = DftTerminatingCharacters
     
   IfEqual, NumPresses,
      NumPresses = 1
     
   IfEqual, ToolTipOffset,
      ToolTipOffset = 14
     
   IfEqual, ArrowKeyMethod,
      ArrowKeyMethod = First
     
   Return
}
   
SaveScript:
; Close the tooltip if it's open
ToolTip,
IfNotEqual, LearnedWords,
{
   ; Add all the standard words to the tempwordlist
   FileRead, ParseWords, %A_ScriptDir%\Wordlist.txt
   IfEqual, LearnMode, On
   {
      LearnedwordsPos := InStr(ParseWords, "`;LEARNEDWORDS`;",true,1) ;Check for Learned Words
   } else {
            LearnedwordsPos = 0 ;force all words to be re-written if we aren't learning
         }
   IfNotEqual, LearnedwordsPos, 0
   {
      TempWordList := SubStr(ParseWords, 1, LearnedwordsPos - 1) ;Grab all non-learned words out of list
   } else {
            TempWordList := ParseWords
         }
   ParseWords =
   ; Parse the learned words and store them in a new list by count if their total count is greater than 5.
   ; Prefix the word with the count and "z" for sorting
   Loop, Parse, LearnedWords, `,
   {
      SortWord := ConvertWordToAscii(A_LoopField,0)
     
      IfGreaterOrEqual, zCount%SortWord%, 5
      {
         SortWordList .= "," . zCount%SortWord% . "z" . A_LoopField
      }
   }
   
   StringTrimLeft, SortWordList, SortWordList, 1 ;remove extra starting comma

   Sort, SortWordList, N R D, ; Sort numerically, comma delimiter

   IfNotEqual, SortWordList, ; If SortWordList exists write to the file, otherwise don't.
   {
      Loop
      {
         StringRight, LastChar, TempWordList, 1
         IF ( ( LastChar = "`r") || ( LastChar = "`n" ) )
         {
            StringTrimRight, TempWordList, TempWordList, 1
         } else {
                  Break
               }
      }
         
      IfEqual, LearnMode, On
         TempWordList .= "`r`n`;LEARNEDWORDS`;" ;only append ;LEARNEDWORDS; if we are in learning mode
      Loop, Parse, SortWordList, `,
      {
         StringTrimLeft, AppendWord, A_LoopField, InStr(A_LoopField,"z") ;Strip (Number,"z") from beginning
         TempWordList .= "`r`n" . AppendWord
      }
   
      FileDelete, %A_ScriptDir%\Temp_Wordlist.txt
      FileAppend, %TempWordList%, %A_ScriptDir%\Temp_Wordlist.txt ;Only update the file if we have learned words
      FileCopy, %A_ScriptDir%\Temp_Wordlist.txt, %A_ScriptDir%\Wordlist.txt, 1
      FileDelete, %A_ScriptDir%\Temp_Wordlist.txt
   }
}

ExitApp
Back to top
View user's profile Send private message
SoLong&Thx4AllTheFish



Joined: 27 May 2007
Posts: 4999

PostPosted: Thu Jan 21, 2010 3:52 pm    Post subject: Reply with quote

Looks nice Cool

two comments + one feature

a) it doesn't seem to run the first time but does the second, probably because it doesn't read the INI?

b) the up/down is nice Smile but I liked my version slightly better for two reason b1: I could cycle through the list b2: when I pressed UP the first time it jumped to the last item in the list which would be the fastest way the select the last item in the list (apart from pressing 0)

New feature suggestion Smile

Because the up/down arrow solves the problem of getting number 13 for example you can increase the number of items shown above 10, the only adaptation needed would be

- configurable number of items
- stop adding digits after first 10 :

Quote:
1 abalone
2 abalones
3 abandon
4 abandoned
5 abandonedly
6 abandonee
7 abandoner
8 abandoners
9 abandoning
0 abandonment
abandonments
abandons
abashedly
abashing


If you'd do that the UP to go to the LAST item would make sense Smile

I'm I driving you insane yet Wink
_________________
AHK Wiki FAQ
TF : Text files & strings lib, TF Forum
Back to top
View user's profile Send private message
maniac



Joined: 28 Aug 2009
Posts: 267

PostPosted: Thu Jan 21, 2010 4:04 pm    Post subject: Reply with quote

hugov wrote:
Looks nice Cool

two comments + one feature

a) it doesn't seem to run the first time but does the second, probably because it doesn't read the INI?

b) the up/down is nice Smile but I liked my version slightly better for two reason b1: I could cycle through the list b2: when I pressed UP the first time it jumped to the last item in the list which would be the fastest way the select the last item in the list (apart from pressing 0)

New feature suggestion Smile

Because the up/down arrow solves the problem of getting number 13 for example you can increase the number of items shown above 10, the only adaptation needed would be

- configurable number of items
- stop adding digits after first 10 :

Quote:
1 abalone
2 abalones
3 abandon
4 abandoned
5 abandonedly
6 abandonee
7 abandoner
8 abandoners
9 abandoning
0 abandonment
abandonments
abandons
abashedly
abashing


If you'd do that the UP to go to the LAST item would make sense Smile

I'm I driving you insane yet Wink


a. Fixed, I forgot to default the LearnMode option if none is used yet

b. Oops! I forgot to mention, PageUp/PageDn work in the list. PgDn brings you to the end and vice-versa. I decided to not use Home/End as those keys are too necessary when programming.

If you still think it should wrap, let me know. I emulated SciTE4AutoHotkey's autocomplete functionality.

Good idea on increasing the number of words, I'll look into that. It should still be limited for performance (tooltip draw) reasons but maybe I could make it a parameter.

Thanks for all the advice Razz

edit:
OK, max items is now 20, min is 10. Defined via MaxMatches parameter in INI file. I tried 30 but got too much slowdown when drawing the tooltip.

Hitting PgUp will bring you up 10 items, stopping at the first.
Hitting PgDn will bring you down 10 items, stopping at the last.

Hitting Up on the first item will bring you to the last item.
Hitting Down in the last item will bring you to the first item.

Let me know what you think.


Code:
;  TypingAid
;  Press 1 to 0 keys to autocomplete the word upon suggestion
;  Or use the Up/Down keys to select an item
;  (0 will match suggestion 10)
;                              Credits:
;                               -Jordi S
;                               -Maniac
;                               -hugov
;                               -kakarukeys
;                               -Asaptrad
;___________________________________________

; Press 1 to 0 keys to autocomplete the word upon suggestion
;___________________________________________

;    CONFIGURATIONS

#NoEnv
SetBatchLines, 20ms
ListLines Off
OnExit, SaveScript

;read in the preferences file
ReadPreferences()

;setup code
clearword=1
MouseX = 0
MouseY = 0
Helper_id =
CoordMode, Mouse, Relative
CoordMode, ToolTip, Relative
AutoTrim, Off

SetTimer, Winchanged, 100

;mark the wordlist as not done
WordListDone = 0

;reads list of words from file
FileRead, ParseWords, %A_ScriptDir%\Wordlist.txt
Loop, Parse, ParseWords, `n, `r
{
   AddWordToList(A_LoopField,0)
}
ParseWords =

;reverse the numbers of the word counts in memory
GoSub, ReverseWordNums

;mark the wordlist as completed
WordlistDone = 1

Loop
{
   ;Editor window check
    WinGetActiveTitle, ATitle
    WinGet, A_id, ID, %ATitle%
    IfNotInString, ATitle, %ETitle%
    {
      ToolTip
      Word=
      WinWaitActive, %ETitle%
      ATitle =
      Continue
   }   
   ATitle =
   
   ;Get one key at a time
   Input, chr, L1 V, %TerminatingCharacters%
   EndKey = %errorlevel%
   ; If active window has different window ID from before the input, blank word
   ; (well, assign the number pressed to the word)   
   WinGet, A_id2, ID, A
   IfNotEqual, A_id, %A_id2%
   {
      Gosub,clearallvars
      Word = %chr%
      Continue
   }
   
   ifequal, OldCaretY,
        OldCaretY := HCaretY()
   if ( OldCaretY != HCaretY() )
   {
      ; add the word if switching lines
      AddWordToList(Word,0)
      Gosub,clearallvars
      Word = %chr%
      Continue         
   }

   OldCaretY := HCaretY()
   
      ;Backspace clears last letter
   ifequal, EndKey, Endkey:BackSpace
   {
      StringLen, len, Word
      IfNotEqual, len, 0
      {
         ifequal, len, 1   
         {
            Gosub,clearallvars
         } else {
                  StringTrimRight, Word, Word, 1
                }     
      }
   } else ifequal, EndKey, Max
         {
            if chr in %ForceNewWordCharacters%
            {
               AddWordToList(Word,0)
               Gosub, clearallvars
               Word = %chr%
               Continue
            } else {
                  Word .= chr
                  }
         } else {
                  AddWordToList(Word,0)
                  Gosub, clearallvars     
                }
   
   ;Wait till minimum letters
   IF ( StrLen(Word) < wlen )
   {
      ToolTip,
      Continue
   }

   IfNotEqual, MatchPos,
   {
      IfEqual, ArrowKeyMethod, LastWord
      {
         OldMatch := singlematch%MatchPos%
      } else {
               IfEqual, ArrowKeyMethod, LastPosition
               {
                  OldMatch = %MatchPos%
               } else {
                        OldMatch =
                     }
            }
   
   } else {
            OldMatch =
         }

   ;Match part-word with command
   Num =
   number = 0
   StringLeft, baseword, Word, %wlen%
   baseword := ConvertWordToAscii(baseword,1)
   Loop
   {
      IfEqual, zword%baseword%%a_index%,, Break
      IfEqual, number, %MaxMatches%
         Break
      if ( SubStr(zword%baseword%%a_index%, 1, StrLen(Word)) = Word )
      {
         number ++
         singlematch := zword%baseword%%a_index%
         singlematch%number% = %singlematch%
           
         Continue           
      }
   }
   
   ;If no match then clear Tip
   IfEqual, number, 0
   {
      clearword=0
      Gosub,clearallvars
      Continue
   }

   IfEqual, OldMatch,
   {
      IfEqual, ArrowKeyMethod, Off
      {
         MatchPos =
      } else {
               MatchPos = 1
            }
   } Else {
            IfEqual, ArrowKeyMethod, Off
            {
               MatchPos =
            } else {
                     IfEqual, ArrowKeyMethod, LastPosition
                     {
                        IfGreater, OldMatch, %Number%
                        {
                           MatchPos = %Number%
                        } else {
                                 MatchPos = %OldMatch%
                              }
                     
                     } else {
                              IfEqual, ArrowKeyMethod, LastWord
                              {
                                 Pos =
                                 Loop, %Number%
                                 {
                                    if ( OldMatch == singlematch%A_Index% )
                                    {
                                    Pos = %A_Index%
                                    Break
                                    }
                                 }
                                 IfEqual, pos,
                                 {
                                    MatchPos = 1
                                 } Else {
                                          MatchPos = %Pos%
                                       }
                              } else {
                                       MatchPos = 1
                                    }
                                 
                           }
                  }
         }   
   OldMatch =
   RebuildMatchList()
   ShowToolTip()
}

RebuildMatchList()
{
   global
   match =
   Loop, %Number%
   {
      AddToMatchList(A_Index,singlematch%A_Index%)
   }
   StringTrimRight, match, match, 1        ; Get rid of the last linefeed
   Return
}

AddToMatchList(position,value)
{
   global
   
   Local prefix
   IfGreater, position, 10
   {
      prefix =
   } else {   
            prefix := Mod(position,10) . ". "
         }

   IfEqual, ArrowKeyMethod, Off
   {
      match .= prefix . value . "`n"
   } else {
            IfEqual, MatchPos, %Position%
            {
               match .= ">" . prefix . value . "`n"
            } Else {
                     match .= "   " . prefix . value . "`n"
                  }
         }
}

;Show matched values
ShowToolTip()
{
   global ToolTipOffset
   global Word
   global Match
   WinGetPos, , PosY, , SizeY, A
   ToolTipSizeY := (number * 12)
   ToolTipPosY := HCaretY()+ToolTipOffset
   ; + ToolTipOffset Move tooltip down a little so as not to hide the caret.
   if ((ToolTipSizeY + ToolTipPosY) > (PosY + SizeY))
       ToolTipPosY := (HCaretY() - ToolTipOffset - ToolTipSizeY)
   IfNotEqual, Word,
      ToolTip, %match%, HCaretX(), %ToolTipPosY%
}
   

; Timed function to detect change of focus (and remove tooltip when changing active window)
Winchanged:
   WinGetActiveTitle, ATitle
   WinGet, A_id3, ID, %ATitle%
   IfNotEqual, A_id, %A_id3%
   {
      ToolTip ,
   } else {
            ; If we are in the correct window, and OldCaretY is set, clear the tooltip if not in the same line
            IfInString, ATitle, %ETitle%
            {
               IfNotEqual, OldCaretY,
               {
                  if ( OldCaretY != HCaretY() )
                  {
                     ToolTip,
                  }
               }
            }
         }
   ATitle =
   A_Id3 =
   Return


; Update last click position in case Caret is not detectable
~LButton::
MouseGetPos, MouseX, MouseY
Return
   
; Key definitions for autocomplete (0 to 9)
#MaxThreadsPerHotkey 1
$1::
$2::
$3::
$4::
$5::
$6::
$7::
$8::
$9::
$0::
CheckWord(A_ThisHotkey)
Return

$^Enter::
$^Space::
$Tab::
$Up::
$Down::
$PgUp::
$PgDn::
EvaluateUpDown(A_ThisHotKey)
Return

^+h::
CreateHelperWindow()
Return

^+c::
AddSelectedWordToList()
Return

; If hotkey was pressed, check wether there's a match going on and send it, otherwise send the number(s) typed
CheckWord(Key)
{
   global
   Local ATitle
   Local A_id2
   Local WordIndex
   Local KeyAgain
   
   StringRight, Key, Key, 1 ;Grab just the number pushed, trim off the "$"
   
   IfEqual, Key, 0
   {
      WordIndex = 10
   } else {
            WordIndex = %Key%
         } 
   
   clearword=1
   IfEqual, NumPresses, 2
      Suspend, On

   ; If active window has different window ID from before the input, blank word
   ; (well, assign the number pressed to the word)
   if ( ReturnWinActive() = )
   {
      SendInput,%key%
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }
   
   if ReturnLineWrong() ;Make sure we are still on the same line
   {
      SendInput,%key%
      Gosub,clearallvars
      IfEqual, NumPresses, 2
      Suspend, Off
      Return
   }

   ifequal, Word,        ; only continue if word is not empty
   {
      SendInput,%key%
      Word = %key%
      clearword=0
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }
         
   ifequal, singlematch%WordIndex%,   ; only continue singlematch is not empty
   {
      SendInput,%key%
      Word .= key
      clearword=0
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }

   IfEqual, NumPresses, 2
   {
      Input, keyagain, L1 I T0.5, 1234567890
     
      ; If there is a timeout, abort replacement, send key and return
      IfEqual, ErrorLevel, Timeout
      {
         SendInput, %key%
         Word .= key
         clearword=0
         Gosub,clearallvars
         Suspend, off
         Return
      }

      ; Make sure it's an EndKey, otherwise abort replacement, send key and return
      IfNotInString, ErrorLevel, EndKey:
      {
         SendInput, %key%%keyagain%
         Word .= key . keyagain
         clearword=0
         Gosub, clearallvars
         Suspend, off
         Return
      }
   
      ; If the 2nd key is NOT the same 1st trigger key, abort replacement and send keys   
      IfNotInString, ErrorLevel, %key%
      {
         StringTrimLeft, keyagain, ErrorLevel, 7
         SendInput, %key%%keyagain%
         Word .= key . keyagain
         clearword=0
         Gosub, clearallvars
         Suspend, Off
         Return
      }
   }

   SendWord(WordIndex)
   IfEqual, NumPresses, 2
      Suspend, Off
   Return
}

SendWord(WordIndex)
{
   global
   Local sending
   Local len
   ;Local ClipboardSave
   ;Send the word
   sending := singlematch%WordIndex%
   StringLen, len, Word
   ; Update Typed Count
   UpdateWordCount(sending,0)
   SendPlay, {BS %len%}{Raw}%sending% ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt)
   ; below works but uses clipboard
   ;ClipboardSave:=ClipboardAll
   ;Clipboard = %sending%
   ;SendPlay, {BS %len%}^v ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt)
   ;Clipboard = %ClipboardSave%
   Gosub clearallvars
   Return


;If a hotkey related to the up/down arrows was pressed
EvaluateUpDown(Key)
{
   global
   
   IfEqual, ArrowKeyMethod, Off
   {
      SendKey(Key)
      Return
   }
   
   IfEqual, Match,
   {
      SendKey(Key)
      Return
   }

   if ( ReturnWinActive() = )
   {
      SendKey(Key)
      Gosub, ClearAllVars
      Return
   }

   if ReturnLineWrong()
   {
      SendKey(Key)
      GoSub, ClearAllVars
      Return
   }   
   
   IfEqual, Word, ; only continue if word is not empty
   {
      SendKey(Key)
      ClearWord = 0
      GoSub, ClearAllVars
      Return
   }
   
   if ( ( Key = "$^Enter" ) || ( Key = "$Tab" ) || ( Key = "$^Space" ) )
   {
     
      IfEqual, singlematch%MatchPos%, ;only continue if singlematch is not empty
      {
         SendKey(Key)
         MatchPos = %Number%
         RebuildMatchList()
         ShowToolTip()
         Return
      }
     
      SendWord(MatchPos)
      Return
     
   }
   IfEqual, Key, $Up
   {   
      MatchPos--
   } else {
            IfEqual, Key, $Down
            {
               MatchPos++
            } else {
                     IfEqual, Key, $PgUp
                     {
                        MatchPos-=10
                        IfLess, MatchPos, 1
                           MatchPos = 1
                     } else {
                              IfEqual, Key, $PgDn
                              {
                                 MatchPos+=10
                                 IfGreater, MatchPos, %Number%
                                    MatchPos = %Number%
                              }
                           }
                  }
         }
   IfLess, MatchPos, 1
   {
      MatchPos = %Number%
   } else {
            IfGreater, MatchPos, %Number%
            {
               MatchPos = 1
            }
         }
   RebuildMatchList()
   ShowToolTip()
   Return
}
           
SendKey(Key)
{
   IfEqual, Key, $^Enter
   {
      Key = ^{Enter}
   } else {
            IfEqual, Key, $^Space
            {
               Key = ^{Space}
            } else {
                     Key := "{" . SubStr(Key, 2) . "}"
                  }
         }
     
   SendInput, %Key%
   Return
}
   
     
     
ReturnWinActive()
{
   global A_id
   WinGet, A_id2, ID, A
   Return, ( A_id = A_id2 )
}

ReturnLineWrong()
{
   global OldCaretY
   Return, ( OldCaretY != HCaretY() )
}

;Create helper window for showing tooltip
CreateHelperWindow()
{
   Global Helper_id
   Gui, Add, Text,,Tooltip appears here
   Gui, Show
   WinGet, Helper_id, ID,,Tooltip appears here
   WinSet, AlwaysOnTop, On, ahk_id %Helper_id%
   return
}

; function to grab the X position of the caret for the tooltip
HCaretX()
{
    global MouseX
    global Helper_id
   
    WinGetPos, HelperX,,,, ahk_id %Helper_id%
    WinGetPos, X,,,, A
   
    if HelperX !=
    {
        if X !=
        {
            return HelperX - X
        }
    }
    if A_CaretX < 14
    {
        if MouseX != 0
        {
            return MouseX
        }
    }
    return A_CaretX
}

; function to grab the Y position of the caret for the tooltip
HCaretY()
{
    global MouseY
    global Helper_id
   
    WinGetPos,,HelperY,,, ahk_id %Helper_id%
    WinGetPos,, Y,,, A
    if HelperY !=
    {
        if Y !=
        {
            return HelperY - Y
        }
    }
    if A_CaretX < 14
    {
        if MouseY != 0
        {
            return MouseY + 20
        }
    }
    return A_CaretY
}

AddSelectedWordToList()
{
   ClipboardSave := ClipboardAll
   Clipboard =
   Sleep, 100
   SendInput, ^c
   ClipWait, 0
   IfNotEqual, Clipboard,
   {
      AddWordToList(Clipboard,1)
   }
   Clipboard = %ClipboardSave%
}

AddWordToList(AddWord,ForceCountNewOnly)
{
   ;AddWord = Word to add to the list
   ;ForceCountNewOnly = force this word to be permanently learned even if learnmode is off
   global
   Local CharTerminateList
   Local Base
   Local AddWordInList
   Local CountWord
   Local pos
   Local LearnModeTemp
   
   IfEqual, LearnMode, On
   {
      LearnModeTemp = 1
   } else {
            IfEqual, ForceCountNewOnly, 1
               LearnModeTemp = 1
         }

   Ifequal, Addword,  ;If we have no word to add, skip out.
      Return
   if ( Substr(addword,1,1) = ";" ) ;If first char is ";", clear word and skip out.
   {
      IfEqual, LearnMode, On ;Check LearnMode here as we only do this if the wordlist is not done
      {
         IfEqual, wordlistdone, 0 ;If we are still reading the wordlist file and we come across ;LEARNEDWORDS; set the LearnedWordsCount flag
         {
            IfEqual, AddWord, `;LEARNEDWORDS`;
               LearnedWordsCount=0
         }
      }
      Return
   }
   IF ( StrLen(addword) <= wlen ) ; don't add the word if it's not longer than the minimum length
   {
      Return
   }
   
   ifequal, wordlistdone, 1
   {
      IfNotEqual, LearnModeTemp, 1
      {
         Return   
      } else {
               if addword contains 1,2,3,4,5,6,7,8,9,0,%ForceNewWordCharacters%
                  Return
            }
   }

   Base := ConvertWordToAscii(SubStr(addword,1,wlen),1)
   IfEqual, WordListDone, 0 ;if this is read from the wordlist
   {
      IfNotEqual,LearnedWordsCount,  ;if this is a stored learned word
      {
         CountWord := ConvertWordToAscii(addword,0)
         IfEqual, LearnedWords,     ;if we haven't learned any words yet, set the LearnedWords list to the new word
         {
            LearnedWords = %addword% 
         } else {   ;otherwise append the learned word to the list
                  LearnedWords .= "," . addword
               }
         zCount%CountWord% := LearnedWordsCount++    ;increment the count and store the Weight of the LearnedWord in reverse order (will be inverted later)
      }
      IncrementCounterAndAddWord(Base,AddWord)
     
   } else { ; If this is an on-the-fly learned word
            AddWordInList =
            Loop ;Check to see if the word is already in the list, case sensitive
            {
               IfEqual, zword%base%%a_index%,, Break
               if ( zword%base%%a_index% == AddWord )
               {
                  AddWordInList = 1
                  Break
               }           
               Continue           
            }
           
            IfEqual, AddWordInList, ; if the word is not in the list
            {
               CountWord := ConvertWordToAscii(addWord,0)
               IfEqual, ForceCountNewOnly, 1
               {
                  zCount%CountWord% = 5 ;set the count to 5 so it gets written to the file
               } else {
                        zCount%CountWord% = 1   ;set the count to one as it's the first time we typed it
                     }
               IfEqual, LearnedWords,    ;if we haven't learned any words yet, set the LearnedWords list to the new word
               {
                  LearnedWords = %addword% 
               } else {   ;otherwise append the learned word to the list
                        LearnedWords .= "," . addword
                     }
               IncrementCounterAndAddWord(Base,AddWord)
               
               IfEqual, LearnMode, On
               {
                  IfEqual, ForceCountNewOnly, 1
                     UpdateWordCount(addword,1) ;resort the necessary words if it's a forced added word
               }
            } else {
                     IfEqual, ForceCountNewOnly, 1
                     {
                        CountWord := ConvertWordToAscii(addWord,0)
                        IF ( zCount%CountWord% < 5 )
                           zCount%CountWord% = 5
                        UpdateWordCount(addWord,1)Icon
                     } else {
                              IfEqual, LearnMode, On
                              {
                                 UpdateWordCount(addword,0) ;Increment the word count if it's already in the list and we aren't forcing it on
                              }
                           }
                  }
         }
   
   Return
}

IncrementCounterAndAddWord(Base,AddWord)
{
   global
   local pos
   ; Increment the counter for each hash
   zbasenum%Base%++       
   pos := zbasenum%Base%
   ; Set the hashed value to the word
   zword%Base%%pos% = %addword%
}
   
; This sub will reverse the read numbers since now we know the total number of words
ReverseWordNums:
LearnedWordsCount+=4
Loop,parse,LearnedWords, `,
{
   AsciiWord := ConvertWordToAscii(A_LoopField,0)
   zCount%AsciiWord% := LearnedWordsCount - zCount%AsciiWord%
}

AsciiWord =
LearnedWordsCount =

Return

UpdateWordCount(word,SortOnly)
{
   ;Word = Word to increment count for
   ;SortOnly = Only sort the words, don't increment the count
   ; If the Count for the word already exists - ie if it's a learned word, increment it, else don't.
   local CountWord := ConvertWordToAscii(word,0)
   IfNotEqual, zCount%CountWord%,
   {
      IfNotEqual, SortOnly, 1 ;don't increment the count if we only want to sort the words
         zCount%CountWord%++ 
      local WordBase
      StringLeft, WordBase, word, %wlen% ;find the pseudohash for the word
      WordBase := ConvertWordToAscii(WordBase,1)
      Local ConvertWord =
      Local LowIndex =
      Local WordList =
      Loop
      {
         ifequal, zword%WordBase%%A_Index%, ;Break the loop if no more words to read for the hash
            Break
         CountWord := zword%WordBase%%A_Index% ;Set CountWord to the current Word position
         ConvertWord := ConvertWordToAscii(CountWord,0) ; Find the Ascii equivalent of the word
         IfNotEqual, zCount%ConvertWord%,  ;If there's no count for this word do nothing
         {
            IfEqual, LowIndex,
               LowIndex = %A_Index% ;If this is the first word we've found with a count set this as our starting position
               
            WordList .= "," . zCount%ConvertWord% . "z" . CountWord ;prefix all words with (zCount"z")
         }
      }
     
      ifnotequal, Wordlist, ;If we have no words to process, don't
      {
         StringTrimLeft, WordList, WordList, 1
         Sort, WordList, N R D, ;Sort the wordlist by order of
         
         LowIndex-- ;A_Index starts at 1 so this value needs to be decremented
         Local IndexPos =
         Loop, Parse, WordList, `,
         {
            IndexPos := LowIndex + A_Index ;Set the current word we are processing to the starting pos plus word position
            StringTrimLeft, CountWord, A_LoopField, InStr(A_LoopField,"z") ;Strip (Number,"z") from beginning
            zword%WordBase%%IndexPos% = %CountWord% ; update the word in the list
           
         }
      }
   }
   Return
}
     
ConvertWordToAscii(Base,Caps)
{
; Return the word in Ascii numbers padded to length 3 per character
; Capitalize the string if NoCaps is not set
   IfEqual, Caps, 1
      StringUpper, Base, Base
   Loop, Parse, Base
   {
      New .= SubStr("00" . Asc(A_LoopField),-2)
   }
Return New
}

; This is to blank all vars related to matches, tooltip and (optionally) word
clearallvars:
      Ifequal,clearword,1
      {
         word =
         OldCaretY=
      }
      ToolTip
      ; Clear all singlematches
      Loop, 10
      {
         singlematch%a_index% =
      }
      sending =
      key=
      match=
      MatchPos=
      clearword=1
      Return

ReadPreferences()
{
   global
   Local Prefs
   Local INI
   
   Prefs = %A_ScriptDir%\Preferences.ini
   DftTerminatingCharacters = {enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,¿?¡!'"()]{}{}}{{}:
   If FileExist(Prefs)
   {
      IniRead, ETitle, %Prefs%, Settings, Title, %A_Space%
      IniRead, TerminatingCharacters, %Prefs%, Settings, TerminatingCharacters, %DftTerminatingCharacters%
      IniRead, ForceNewWordCharacters, %Prefs%, Settings, ForceNewWordCharacters, %A_Space%
      IniRead, Wlen, %Prefs%, Settings, Length, 3
      IniRead, NumPresses, %Prefs%, Settings, NumPresses, 1
      IniRead, LearnMode, %Prefs%, Settings, LearnMode, Off
      IniRead, ToolTipOffset, %Prefs%, Settings, ToolTipOffset, 14
      IniRead, ArrowKeyMethod, %Prefs%, Settings, ArrowKeyMethod, First
      IniRead, MaxMatches, %Prefs%, Settings, MaxMatches, 10
   } else {
            INI=
               (
;Title is a string of text to find in the title of the window you want TypingAid enabled for. If you leave
;it blank it will work in all windows.
[Settings]
Title=
;
;TerminatingCharacters is a list of EndKey characters which will signal the script that you are done typing a word.
;A list of keys may be found here:
; http://www.autohotkey.com/docs/KeyList.htm
;Certain characters (such as `% or , ) require an escape character in front, see here:
; http://www.autohotkey.com/docs/commands/_EscapeChar.htm
;For more detail on how to format the list of characters please see the EndKeys section (paragraphs 2,3,4) of:
; http://www.autohotkey.com/docs/commands/Input.htm
TerminatingCharacters={enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,:¿?¡!'"()]{}{}}{{}
;
;ForceNewWordCharacters is a comma separated list of characters which force the script to start a new word whenever
;one of those characters is typed. Any words which begin with one of these characters will never be learned (even
;if learning is enabled). If you were typing a word when you hit one of these characters that word will be learned
;if learning is enabled.
ForceNewWordCharacters=
;
;Length is the minimum number of characters required to show the list of words. The higher this number the better performance will be.
Length=3
;
;NumPresses is the number of times the hotkey must be pushed for the word to be selected, either 1 or 2.
NumPresses=1
;
;LearnMode defines whether or not the script should learn new words as you type them, either On or Off
LearnMode=On
;
;ToolTipOffset is the number of pixels below the top of the Caret (Cursor) to display the tooltip
ToolTipOffset=14
;
;ArrowKeyMethod is the way the arrow keys are handled in the drop down. Off means you can only use the number keys
;First resets the selected word to the beginning whenever you type a new character
;LastWord keeps the last word selected if still in the last, else resets to the beginning
;LastPosition keeps the last cursor position
ArrowKeyMethod=First
;
;MaxMatches is the number of words to show in the dropdown. Valid range is from 10 to 20. If ArrowKeyMethod=Off
;this will be forced to 10
MaxMatches=10
               )               
               FileAppend, %INI%, %Prefs%
         }
   
   IfEqual, TerminatingCharacters,
      TerminatingCharacters = DftTerminatingCharacters
     
   IfEqual, Wlen,
      Wlen = 3
     
   IfEqual, NumPresses,
      NumPresses = 1
     
   IfEqual, LearnMode,
      LearnMode = Off
     
   IfEqual, ToolTipOffset,
      ToolTipOffset = 14
     
   IfEqual, ArrowKeyMethod,
   {
      ArrowKeyMethod = First       
   } else {
            IfEqual, ArrowKeyMethod, Off ;force MaxMatches to 10 if we aren't using arrow keys
               MaxMatches=10
         }
   
   IfLess, MaxMatches, 10
   {
      MaxMatches=10
   } else {
            IfGreater, MaxMatches, 20
               MaxMatches = 20
         }
     
   Return
}
   
SaveScript:
; Close the tooltip if it's open
ToolTip,
IfNotEqual, LearnedWords,
{
   ; Add all the standard words to the tempwordlist
   FileRead, ParseWords, %A_ScriptDir%\Wordlist.txt
   IfEqual, LearnMode, On
   {
      LearnedwordsPos := InStr(ParseWords, "`;LEARNEDWORDS`;",true,1) ;Check for Learned Words
   } else {
            LearnedwordsPos = 0 ;force all words to be re-written if we aren't learning
         }
   IfNotEqual, LearnedwordsPos, 0
   {
      TempWordList := SubStr(ParseWords, 1, LearnedwordsPos - 1) ;Grab all non-learned words out of list
   } else {
            TempWordList := ParseWords
         }
   ParseWords =
   ; Parse the learned words and store them in a new list by count if their total count is greater than 5.
   ; Prefix the word with the count and "z" for sorting
   Loop, Parse, LearnedWords, `,
   {
      SortWord := ConvertWordToAscii(A_LoopField,0)
     
      IfGreaterOrEqual, zCount%SortWord%, 5
      {
         SortWordList .= "," . zCount%SortWord% . "z" . A_LoopField
      }
   }
   
   StringTrimLeft, SortWordList, SortWordList, 1 ;remove extra starting comma

   Sort, SortWordList, N R D, ; Sort numerically, comma delimiter

   IfNotEqual, SortWordList, ; If SortWordList exists write to the file, otherwise don't.
   {
      Loop
      {
         StringRight, LastChar, TempWordList, 1
         IF ( ( LastChar = "`r") || ( LastChar = "`n" ) )
         {
            StringTrimRight, TempWordList, TempWordList, 1
         } else {
                  Break
               }
      }
         
      IfEqual, LearnMode, On
         TempWordList .= "`r`n`;LEARNEDWORDS`;" ;only append ;LEARNEDWORDS; if we are in learning mode
      Loop, Parse, SortWordList, `,
      {
         StringTrimLeft, AppendWord, A_LoopField, InStr(A_LoopField,"z") ;Strip (Number,"z") from beginning
         TempWordList .= "`r`n" . AppendWord
      }
   
      FileDelete, %A_ScriptDir%\Temp_Wordlist.txt
      FileAppend, %TempWordList%, %A_ScriptDir%\Temp_Wordlist.txt ;Only update the file if we have learned words
      FileCopy, %A_ScriptDir%\Temp_Wordlist.txt, %A_ScriptDir%\Wordlist.txt, 1
      FileDelete, %A_ScriptDir%\Temp_Wordlist.txt
   }
}

ExitApp


Last edited by maniac on Thu Jan 21, 2010 4:32 pm; edited 1 time in total
Back to top
View user's profile Send private message
SoLong&Thx4AllTheFish



Joined: 27 May 2007
Posts: 4999

PostPosted: Thu Jan 21, 2010 4:11 pm    Post subject: Reply with quote

maniac wrote:
b. Oops! I forgot to mention, PageUp/PageDn work in the list. PgDn brings you to the end and vice-versa. I decided to not use Home/End as those keys are too necessary when programming.
Nice!
Quote:
If you still think it should wrap, let me know. I emulated SciTE4AutoHotkey's autocomplete functionality.
Perhaps make it optional Very Happy some may like it, some may not.

Its turning out quite nicely this script Cool
_________________
AHK Wiki FAQ
TF : Text files & strings lib, TF Forum
Back to top
View user's profile Send private message
maniac



Joined: 28 Aug 2009
Posts: 267

PostPosted: Thu Jan 21, 2010 4:32 pm    Post subject: Reply with quote

I ninja-edited my post, check out the new version Smile.

hugov wrote:
Its turning out quite nicely this script Cool


Thanks Cool
Back to top
View user's profile Send private message
SoLong&Thx4AllTheFish



Joined: 27 May 2007
Posts: 4999

PostPosted: Thu Jan 21, 2010 4:34 pm    Post subject: Reply with quote

Wicked Cool
_________________
AHK Wiki FAQ
TF : Text files & strings lib, TF Forum
Back to top
View user's profile Send private message
maniac



Joined: 28 Aug 2009
Posts: 267

PostPosted: Thu Jan 21, 2010 4:47 pm    Post subject: Reply with quote

Kakarukeys, what do you think of the proposed default terminating characters?

Also, should I call this TypingAid v2.0 when we create the new topic in Scripts and Functions? It's up to you I guess, let me know how you want to handle it.
Back to top
View user's profile Send private message
maniac



Joined: 28 Aug 2009
Posts: 267

PostPosted: Thu Jan 21, 2010 6:03 pm    Post subject: Reply with quote

Cleaned up the preferences file, added 2 more parameters:
1. LearnCount - defines the number of times you have to type a word for it to be learned permanently (dft is 5)
2. DisabledAutoCompleteKeys - lets you turn off Ctrl+Space, Tab, or Ctrl+Enter at will. I realized I need to be able to hit tab at certain times in the programming language I use that this would interfere with.

Fixed a bug with regards to SendKey()... it now uses Send instead of SendInput so Enter/Tab/Space is processed by the script.

Code:
;  TypingAid
;  Press 1 to 0 keys to autocomplete the word upon suggestion
;  Or use the Up/Down keys to select an item
;  (0 will match suggestion 10)
;                              Credits:
;                               -Jordi S
;                               -Maniac
;                               -hugov
;                               -kakarukeys
;                               -Asaptrad
;___________________________________________

; Press 1 to 0 keys to autocomplete the word upon suggestion
;___________________________________________

;    CONFIGURATIONS

#NoEnv
SetBatchLines, 20ms
ListLines Off
OnExit, SaveScript

;read in the preferences file
ReadPreferences()

;setup code
clearword=1
MouseX = 0
MouseY = 0
Helper_id =
CoordMode, Mouse, Relative
CoordMode, ToolTip, Relative
AutoTrim, Off

SetTimer, Winchanged, 100

;mark the wordlist as not done
WordListDone = 0

;reads list of words from file
FileRead, ParseWords, %A_ScriptDir%\Wordlist.txt
Loop, Parse, ParseWords, `n, `r
{
   AddWordToList(A_LoopField,0)
}
ParseWords =

;reverse the numbers of the word counts in memory
GoSub, ReverseWordNums

;mark the wordlist as completed
WordlistDone = 1

Loop
{
   ;Editor window check
    WinGetActiveTitle, ATitle
    WinGet, A_id, ID, %ATitle%
    IfNotInString, ATitle, %ETitle%
    {
      ToolTip
      Word=
      WinWaitActive, %ETitle%
      ATitle =
      Continue
   }   
   ATitle =
   
   ;Get one key at a time
   Input, chr, L1 V, %TerminatingCharacters%
   EndKey = %errorlevel%
   ; If active window has different window ID from before the input, blank word
   ; (well, assign the number pressed to the word)   
   WinGet, A_id2, ID, A
   IfNotEqual, A_id, %A_id2%
   {
      Gosub,clearallvars
      Word = %chr%
      Continue
   }
   
   ifequal, OldCaretY,
        OldCaretY := HCaretY()
   if ( OldCaretY != HCaretY() )
   {
      ; add the word if switching lines
      AddWordToList(Word,0)
      Gosub,clearallvars
      Word = %chr%
      Continue         
   }

   OldCaretY := HCaretY()
   
      ;Backspace clears last letter
   ifequal, EndKey, Endkey:BackSpace
   {
      StringLen, len, Word
      IfNotEqual, len, 0
      {
         ifequal, len, 1   
         {
            Gosub,clearallvars
         } else {
                  StringTrimRight, Word, Word, 1
                }     
      }
   } else ifequal, EndKey, Max
         {
            if chr in %ForceNewWordCharacters%
            {
               AddWordToList(Word,0)
               Gosub, clearallvars
               Word = %chr%
               Continue
            } else {
                  Word .= chr
                  }
         } else {
                  AddWordToList(Word,0)
                  Gosub, clearallvars     
                }
   
   ;Wait till minimum letters
   IF ( StrLen(Word) < wlen )
   {
      ToolTip,
      Continue
   }

   IfNotEqual, MatchPos,
   {
      IfEqual, ArrowKeyMethod, LastWord
      {
         OldMatch := singlematch%MatchPos%
      } else {
               IfEqual, ArrowKeyMethod, LastPosition
               {
                  OldMatch = %MatchPos%
               } else {
                        OldMatch =
                     }
            }
   
   } else {
            OldMatch =
         }

   ;Match part-word with command
   Num =
   number = 0
   StringLeft, baseword, Word, %wlen%
   baseword := ConvertWordToAscii(baseword,1)
   Loop
   {
      IfEqual, zword%baseword%%a_index%,, Break
      IfEqual, number, %MaxMatches%
         Break
      if ( SubStr(zword%baseword%%a_index%, 1, StrLen(Word)) = Word )
      {
         number ++
         singlematch := zword%baseword%%a_index%
         singlematch%number% = %singlematch%
           
         Continue           
      }
   }
   
   ;If no match then clear Tip
   IfEqual, number, 0
   {
      clearword=0
      Gosub,clearallvars
      Continue
   }

   IfEqual, OldMatch,
   {
      IfEqual, ArrowKeyMethod, Off
      {
         MatchPos =
      } else {
               MatchPos = 1
            }
   } Else {
            IfEqual, ArrowKeyMethod, Off
            {
               MatchPos =
            } else {
                     IfEqual, ArrowKeyMethod, LastPosition
                     {
                        IfGreater, OldMatch, %Number%
                        {
                           MatchPos = %Number%
                        } else {
                                 MatchPos = %OldMatch%
                              }
                     
                     } else {
                              IfEqual, ArrowKeyMethod, LastWord
                              {
                                 Pos =
                                 Loop, %Number%
                                 {
                                    if ( OldMatch == singlematch%A_Index% )
                                    {
                                    Pos = %A_Index%
                                    Break
                                    }
                                 }
                                 IfEqual, pos,
                                 {
                                    MatchPos = 1
                                 } Else {
                                          MatchPos = %Pos%
                                       }
                              } else {
                                       MatchPos = 1
                                    }
                                 
                           }
                  }
         }   
   OldMatch =
   RebuildMatchList()
   ShowToolTip()
}

RebuildMatchList()
{
   global
   match =
   Loop, %Number%
   {
      AddToMatchList(A_Index,singlematch%A_Index%)
   }
   StringTrimRight, match, match, 1        ; Get rid of the last linefeed
   Return
}

AddToMatchList(position,value)
{
   global
   
   Local prefix
   IfGreater, position, 10
   {
      prefix =
   } else {   
            prefix := Mod(position,10) . ". "
         }

   IfEqual, ArrowKeyMethod, Off
   {
      match .= prefix . value . "`n"
   } else {
            IfEqual, MatchPos, %Position%
            {
               match .= ">" . prefix . value . "`n"
            } Else {
                     match .= "   " . prefix . value . "`n"
                  }
         }
}

;Show matched values
ShowToolTip()
{
   global ToolTipOffset
   global Word
   global Match
   WinGetPos, , PosY, , SizeY, A
   ToolTipSizeY := (number * 12)
   ToolTipPosY := HCaretY()+ToolTipOffset
   ; + ToolTipOffset Move tooltip down a little so as not to hide the caret.
   if ((ToolTipSizeY + ToolTipPosY) > (PosY + SizeY))
       ToolTipPosY := (HCaretY() - ToolTipOffset - ToolTipSizeY)
   IfNotEqual, Word,
      ToolTip, %match%, HCaretX(), %ToolTipPosY%
}
   

; Timed function to detect change of focus (and remove tooltip when changing active window)
Winchanged:
   WinGetActiveTitle, ATitle
   WinGet, A_id3, ID, %ATitle%
   IfNotEqual, A_id, %A_id3%
   {
      ToolTip ,
   } else {
            ; If we are in the correct window, and OldCaretY is set, clear the tooltip if not in the same line
            IfInString, ATitle, %ETitle%
            {
               IfNotEqual, OldCaretY,
               {
                  if ( OldCaretY != HCaretY() )
                  {
                     ToolTip,
                  }
               }
            }
         }
   ATitle =
   A_Id3 =
   Return


; Update last click position in case Caret is not detectable
~LButton::
MouseGetPos, MouseX, MouseY
Return
   
; Key definitions for autocomplete (0 to 9)
#MaxThreadsPerHotkey 1
$1::
$2::
$3::
$4::
$5::
$6::
$7::
$8::
$9::
$0::
CheckWord(A_ThisHotkey)
Return

$^Enter::
$^Space::
$Tab::
$Up::
$Down::
$PgUp::
$PgDn::
EvaluateUpDown(A_ThisHotKey)
Return

^+h::
CreateHelperWindow()
Return

^+c::
AddSelectedWordToList()
Return

; If hotkey was pressed, check wether there's a match going on and send it, otherwise send the number(s) typed
CheckWord(Key)
{
   global
   Local ATitle
   Local A_id2
   Local WordIndex
   Local KeyAgain
   
   StringRight, Key, Key, 1 ;Grab just the number pushed, trim off the "$"
   
   IfEqual, Key, 0
   {
      WordIndex = 10
   } else {
            WordIndex = %Key%
         } 
   
   clearword=1
   IfEqual, NumPresses, 2
      Suspend, On

   ; If active window has different window ID from before the input, blank word
   ; (well, assign the number pressed to the word)
   if ( ReturnWinActive() = )
   {
      SendInput,%key%
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }
   
   if ReturnLineWrong() ;Make sure we are still on the same line
   {
      SendInput,%key%
      Gosub,clearallvars
      IfEqual, NumPresses, 2
      Suspend, Off
      Return
   }

   ifequal, Word,        ; only continue if word is not empty
   {
      SendInput,%key%
      Word = %key%
      clearword=0
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }
         
   ifequal, singlematch%WordIndex%,   ; only continue singlematch is not empty
   {
      SendInput,%key%
      Word .= key
      clearword=0
      Gosub,clearallvars
      IfEqual, NumPresses, 2
         Suspend, Off
      Return
   }

   IfEqual, NumPresses, 2
   {
      Input, keyagain, L1 I T0.5, 1234567890
     
      ; If there is a timeout, abort replacement, send key and return
      IfEqual, ErrorLevel, Timeout
      {
         SendInput, %key%
         Word .= key
         clearword=0
         Gosub,clearallvars
         Suspend, off
         Return
      }

      ; Make sure it's an EndKey, otherwise abort replacement, send key and return
      IfNotInString, ErrorLevel, EndKey:
      {
         SendInput, %key%%keyagain%
         Word .= key . keyagain
         clearword=0
         Gosub, clearallvars
         Suspend, off
         Return
      }
   
      ; If the 2nd key is NOT the same 1st trigger key, abort replacement and send keys   
      IfNotInString, ErrorLevel, %key%
      {
         StringTrimLeft, keyagain, ErrorLevel, 7
         SendInput, %key%%keyagain%
         Word .= key . keyagain
         clearword=0
         Gosub, clearallvars
         Suspend, Off
         Return
      }
   }

   SendWord(WordIndex)
   IfEqual, NumPresses, 2
      Suspend, Off
   Return
}

SendWord(WordIndex)
{
   global
   Local sending
   Local len
   ;Local ClipboardSave
   ;Send the word
   sending := singlematch%WordIndex%
   StringLen, len, Word
   ; Update Typed Count
   UpdateWordCount(sending,0)
   SendPlay, {BS %len%}{Raw}%sending% ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt)
   ; below works but uses clipboard
   ;ClipboardSave:=ClipboardAll
   ;Clipboard = %sending%
   ;SendPlay, {BS %len%}^v ; First do the backspaces, Then send word (Raw because we want the string exactly as in wordlist.txt)
   ;Clipboard = %ClipboardSave%
   Gosub clearallvars
   Return


;If a hotkey related to the up/down arrows was pressed
EvaluateUpDown(Key)
{
   global
   
   IfEqual, ArrowKeyMethod, Off
   {
      SendKey(Key)
      Return
   }
   
   IfEqual, Match,
   {
      SendKey(Key)
      Return
   }

   if ( ReturnWinActive() = )
   {
      SendKey(Key)
      Gosub, ClearAllVars
      Return
   }

   if ReturnLineWrong()
   {
      SendKey(Key)
      GoSub, ClearAllVars
      Return
   }   
   
   IfEqual, Word, ; only continue if word is not empty
   {
      SendKey(Key)
      ClearWord = 0
      GoSub, ClearAllVars
      Return
   }
   
   if ( ( Key = "$^Enter" ) || ( Key = "$Tab" ) || ( Key = "$^Space" ) )
   {
      Local KeyTest
      IfEqual, Key, $^Enter
      {
         KeyTest = E
      } else {
               IfEqual, Key, $Tab
               {
                  KeyTest = T
               } else {
                        IfEqual, Key, $^Space
                           KeyTest = S
                     }
            }
     
      if DisabledAutoCompleteKeys contains %KeyTest%
      {
         SendKey(Key)
         Return     
      }
     
      IfEqual, singlematch%MatchPos%, ;only continue if singlematch is not empty
      {
         SendKey(Key)
         MatchPos = %Number%
         RebuildMatchList()
         ShowToolTip()
         Return
      }
     
      SendWord(MatchPos)
      Return
     
   }
   IfEqual, Key, $Up
   {   
      MatchPos--
   } else {
            IfEqual, Key, $Down
            {
               MatchPos++
            } else {
                     IfEqual, Key, $PgUp
                     {
                        MatchPos-=10
                        IfLess, MatchPos, 1
                           MatchPos = 1
                     } else {
                              IfEqual, Key, $PgDn
                              {
                                 MatchPos+=10
                                 IfGreater, MatchPos, %Number%
                                    MatchPos = %Number%
                              }
                           }
                  }
         }
   IfLess, MatchPos, 1
   {
      MatchPos = %Number%
   } else {
            IfGreater, MatchPos, %Number%
            {
               MatchPos = 1
            }
         }
   RebuildMatchList()
   ShowToolTip()
   Return
}
           
SendKey(Key)
{
   IfEqual, Key, $^Enter
   {
      Key = ^{Enter}
   } else {
            IfEqual, Key, $^Space
            {
               Key = ^{Space}
            } else {
                     Key := "{" . SubStr(Key, 2) . "}"
                  }
         }
     
   Send, %Key%
   Return
}
   
     
     
ReturnWinActive()
{
   global A_id
   WinGet, A_id2, ID, A
   Return, ( A_id = A_id2 )
}

ReturnLineWrong()
{
   global OldCaretY
   Return, ( OldCaretY != HCaretY() )
}

;Create helper window for showing tooltip
CreateHelperWindow()
{
   Global Helper_id
   Gui, Add, Text,,Tooltip appears here
   Gui, Show
   WinGet, Helper_id, ID,,Tooltip appears here
   WinSet, AlwaysOnTop, On, ahk_id %Helper_id%
   return
}

; function to grab the X position of the caret for the tooltip
HCaretX()
{
    global MouseX
    global Helper_id
   
    WinGetPos, HelperX,,,, ahk_id %Helper_id%
    WinGetPos, X,,,, A
   
    if HelperX !=
    {
        if X !=
        {
            return HelperX - X
        }
    }
    if A_CaretX < 14
    {
        if MouseX != 0
        {
            return MouseX
        }
    }
    return A_CaretX
}

; function to grab the Y position of the caret for the tooltip
HCaretY()
{
    global MouseY
    global Helper_id
   
    WinGetPos,,HelperY,,, ahk_id %Helper_id%
    WinGetPos,, Y,,, A
    if HelperY !=
    {
        if Y !=
        {
            return HelperY - Y
        }
    }
    if A_CaretX < 14
    {
        if MouseY != 0
        {
            return MouseY + 20
        }
    }
    return A_CaretY
}

AddSelectedWordToList()
{
   ClipboardSave := ClipboardAll
   Clipboard =
   Sleep, 100
   SendInput, ^c
   ClipWait, 0
   IfNotEqual, Clipboard,
   {
      AddWordToList(Clipboard,1)
   }
   Clipboard = %ClipboardSave%
}

AddWordToList(AddWord,ForceCountNewOnly)
{
   ;AddWord = Word to add to the list
   ;ForceCountNewOnly = force this word to be permanently learned even if learnmode is off
   global
   Local CharTerminateList
   Local Base
   Local AddWordInList
   Local CountWord
   Local pos
   Local LearnModeTemp
   
   IfEqual, LearnMode, On
   {
      LearnModeTemp = 1
   } else {
            IfEqual, ForceCountNewOnly, 1
               LearnModeTemp = 1
         }

   Ifequal, Addword,  ;If we have no word to add, skip out.
      Return
   if ( Substr(addword,1,1) = ";" ) ;If first char is ";", clear word and skip out.
   {
      IfEqual, LearnMode, On ;Check LearnMode here as we only do this if the wordlist is not done
      {
         IfEqual, wordlistdone, 0 ;If we are still reading the wordlist file and we come across ;LEARNEDWORDS; set the LearnedWordsCount flag
         {
            IfEqual, AddWord, `;LEARNEDWORDS`;
               LearnedWordsCount=0
         }
      }
      Return
   }
   IF ( StrLen(addword) <= wlen ) ; don't add the word if it's not longer than the minimum length
   {
      Return
   }
   
   ifequal, wordlistdone, 1
   {
      IfNotEqual, LearnModeTemp, 1
      {
         Return   
      } else {
               if addword contains 1,2,3,4,5,6,7,8,9,0,%ForceNewWordCharacters%
                  Return
            }
   }

   Base := ConvertWordToAscii(SubStr(addword,1,wlen),1)
   IfEqual, WordListDone, 0 ;if this is read from the wordlist
   {
      IfNotEqual,LearnedWordsCount,  ;if this is a stored learned word
      {
         CountWord := ConvertWordToAscii(addword,0)
         IfEqual, LearnedWords,     ;if we haven't learned any words yet, set the LearnedWords list to the new word
         {
            LearnedWords = %addword% 
         } else {   ;otherwise append the learned word to the list
                  LearnedWords .= "," . addword
               }
         zCount%CountWord% := LearnedWordsCount++    ;increment the count and store the Weight of the LearnedWord in reverse order (will be inverted later)
      }
      IncrementCounterAndAddWord(Base,AddWord)
     
   } else { ; If this is an on-the-fly learned word
            AddWordInList =
            Loop ;Check to see if the word is already in the list, case sensitive
            {
               IfEqual, zword%base%%a_index%,, Break
               if ( zword%base%%a_index% == AddWord )
               {
                  AddWordInList = 1
                  Break
               }           
               Continue           
            }
           
            IfEqual, AddWordInList, ; if the word is not in the list
            {
               CountWord := ConvertWordToAscii(addWord,0)
               IfEqual, ForceCountNewOnly, 1
               {
                  zCount%CountWord% = %LearnCount% ;set the count to LearnCount so it gets written to the file
               } else {
                        zCount%CountWord% = 1   ;set the count to one as it's the first time we typed it
                     }
               IfEqual, LearnedWords,    ;if we haven't learned any words yet, set the LearnedWords list to the new word
               {
                  LearnedWords = %addword% 
               } else {   ;otherwise append the learned word to the list
                        LearnedWords .= "," . addword
                     }
               IncrementCounterAndAddWord(Base,AddWord)
               
               IfEqual, LearnMode, On
               {
                  IfEqual, ForceCountNewOnly, 1
                     UpdateWordCount(addword,1) ;resort the necessary words if it's a forced added word
               }
            } else {
                     IfEqual, ForceCountNewOnly, 1
                     {
                        CountWord := ConvertWordToAscii(addWord,0)
                        IF ( zCount%CountWord% < LearnCount )
                           zCount%CountWord% = %LearnCount%
                        UpdateWordCount(addWord,1)Icon
                     } else {
                              IfEqual, LearnMode, On
                              {
                                 UpdateWordCount(addword,0) ;Increment the word count if it's already in the list and we aren't forcing it on
                              }
                           }
                  }
         }
   
   Return
}

IncrementCounterAndAddWord(Base,AddWord)
{
   global
   local pos
   ; Increment the counter for each hash
   zbasenum%Base%++       
   pos := zbasenum%Base%
   ; Set the hashed value to the word
   zword%Base%%pos% = %addword%
}
   
; This sub will reverse the read numbers since now we know the total number of words
ReverseWordNums:
LearnedWordsCount+=4
Loop,parse,LearnedWords, `,
{
   AsciiWord := ConvertWordToAscii(A_LoopField,0)
   zCount%AsciiWord% := LearnedWordsCount - zCount%AsciiWord%
}

AsciiWord =
LearnedWordsCount =

Return

UpdateWordCount(word,SortOnly)
{
   ;Word = Word to increment count for
   ;SortOnly = Only sort the words, don't increment the count
   ; If the Count for the word already exists - ie if it's a learned word, increment it, else don't.
   local CountWord := ConvertWordToAscii(word,0)
   IfNotEqual, zCount%CountWord%,
   {
      IfNotEqual, SortOnly, 1 ;don't increment the count if we only want to sort the words
         zCount%CountWord%++ 
      local WordBase
      StringLeft, WordBase, word, %wlen% ;find the pseudohash for the word
      WordBase := ConvertWordToAscii(WordBase,1)
      Local ConvertWord =
      Local LowIndex =
      Local WordList =
      Loop
      {
         ifequal, zword%WordBase%%A_Index%, ;Break the loop if no more words to read for the hash
            Break
         CountWord := zword%WordBase%%A_Index% ;Set CountWord to the current Word position
         ConvertWord := ConvertWordToAscii(CountWord,0) ; Find the Ascii equivalent of the word
         IfNotEqual, zCount%ConvertWord%,  ;If there's no count for this word do nothing
         {
            IfEqual, LowIndex,
               LowIndex = %A_Index% ;If this is the first word we've found with a count set this as our starting position
               
            WordList .= "," . zCount%ConvertWord% . "z" . CountWord ;prefix all words with (zCount"z")
         }
      }
     
      ifnotequal, Wordlist, ;If we have no words to process, don't
      {
         StringTrimLeft, WordList, WordList, 1
         Sort, WordList, N R D, ;Sort the wordlist by order of
         
         LowIndex-- ;A_Index starts at 1 so this value needs to be decremented
         Local IndexPos =
         Loop, Parse, WordList, `,
         {
            IndexPos := LowIndex + A_Index ;Set the current word we are processing to the starting pos plus word position
            StringTrimLeft, CountWord, A_LoopField, InStr(A_LoopField,"z") ;Strip (Number,"z") from beginning
            zword%WordBase%%IndexPos% = %CountWord% ; update the word in the list
           
         }
      }
   }
   Return
}
     
ConvertWordToAscii(Base,Caps)
{
; Return the word in Ascii numbers padded to length 3 per character
; Capitalize the string if NoCaps is not set
   IfEqual, Caps, 1
      StringUpper, Base, Base
   Loop, Parse, Base
   {
      New .= SubStr("00" . Asc(A_LoopField),-2)
   }
Return New
}

; This is to blank all vars related to matches, tooltip and (optionally) word
clearallvars:
      Ifequal,clearword,1
      {
         word =
         OldCaretY=
      }
      ToolTip
      ; Clear all singlematches
      Loop, 10
      {
         singlematch%a_index% =
      }
      sending =
      key=
      match=
      MatchPos=
      clearword=1
      Return

ReadPreferences()
{
   global
   Local Prefs
   Local INI
   
   Prefs = %A_ScriptDir%\Preferences.ini
   DftTerminatingCharacters = {enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,¿?¡!'"()]{}{}}{{}:
   If FileExist(Prefs)
   {
      IniRead, ETitle, %Prefs%, Settings, Title, %A_Space%
      IniRead, Wlen, %Prefs%, Settings, Length, 3
      IniRead, NumPresses, %Prefs%, Settings, NumPresses, 1
      IniRead, ToolTipOffset, %Prefs%, Settings, ToolTipOffset, 14
      IniRead, LearnMode, %Prefs%, Settings, LearnMode, Off
      IniRead, LearnCount, %Prefs%, Settings, LearnCount, 5
      IniRead, ArrowKeyMethod, %Prefs%, Settings, ArrowKeyMethod, First
      IniRead, DisabledAutoCompleteKeys, %Prefs%, Settings, DisabledAutoCompleteKeys, %A_Space%
      IniRead, MaxMatches, %Prefs%, Settings, MaxMatches, 10
      IniRead, TerminatingCharacters, %Prefs%, Settings, TerminatingCharacters, %DftTerminatingCharacters%
      IniRead, ForceNewWordCharacters, %Prefs%, Settings, ForceNewWordCharacters, %A_Space%
   } else {
            INI=
               (
[Settings]
;
;Title is a string of text to find in the title of the window you want TypingAid enabled for. If you leave it blank it will
;work in all windows.
Title=
;
;Length is the minimum number of characters required to show the list of words. The higher this number the better performance will be.
Length=3
;
;NumPresses is the number of times the hotkey must be pushed for the word to be selected, either 1 or 2.
NumPresses=1
;
;Number of pixels below the top of the Caret (Cursor) to display the tooltip
ToolTipOffset=14
;
;LearnMode defines whether or not the script should learn new words as you type them, either On or Off
LearnMode=On
;
;LearnCount defines the number of times you have to type a word within a single session for it to be learned permanently
LearnCount=5
;
;ArrowKeyMethod is the way the arrow keys are handled in the drop down.
;Options are:
;  Off - you can only use the number keys
;  First - resets the selected word to the beginning whenever you type a new character
;  LastWord - keeps the last word selected if still in the last, else resets to the beginning
;  LastPosition - keeps the last cursor position
ArrowKeyMethod=First
;
;DisabledAutoCompleteKeys is used to disable certain keys from autocompleting the selected item in the dropdown.
;Place the character listed for each key you want to disable in the list. IE
;DisabledAutoCompleteKeys=ST
;would disable Ctrl+Space and Tab.
;  E = Ctrl + Enter
;  S = Ctrl + Space
;  T = Tab
DisabledAutoCompleteKeys=
;
;MaxMatches is the number of words to show in the dropdown. Valid range is from 10 to 20.
;If ArrowKeyMethod=Off this will be forced to 10
MaxMatches=10
;
;TerminatingCharacters is a list of EndKey characters which will signal the script that you are done typing a word.
;A list of keys may be found here:
; http://www.autohotkey.com/docs/KeyList.htm
;Certain characters (such as `% or , ) require an escape character in front, see here:
; http://www.autohotkey.com/docs/commands/_EscapeChar.htm
;For more detail on how to format the list of characters please see the EndKeys section (paragraphs 2,3,4) of:
; http://www.autohotkey.com/docs/commands/Input.htm
TerminatingCharacters={enter}{space}{bs}{esc}{tab}{Home}{End}{PgUp}{PgDn}{Up}{Down}{Left}{Right}.;`,¿?¡!'"()]{}{}}{{}:
;
;ForceNewWordCharacters is a comma separated list of characters which force the script to start a new word whenever
;one of those characters is typed. Any words which begin with one of these characters will never be learned (even
;if learning is enabled). If you were typing a word when you hit one of these characters that word will be learned
;if learning is enabled.
ForceNewWordCharacters=
               )               
               FileAppend, %INI%, %Prefs%
         }
   
   IfEqual, Wlen,
      Wlen = 3
     
   IfEqual, NumPresses,
      NumPresses = 1
     
   IfEqual, ToolTipOffset,
      ToolTipOffset = 14
     
   IfEqual, LearnMode,
      LearnMode = Off
   
   IfEqual, LearnCount,
      LearnCount = 5
     
   IfEqual, ArrowKeyMethod,
   {
      ArrowKeyMethod = First       
   } else {
            IfEqual, ArrowKeyMethod, Off ;force MaxMatches to 10 if we aren't using arrow keys
               MaxMatches=10
         }
   
   IfLess, MaxMatches, 10
   {
      MaxMatches=10
   } else {
            IfGreater, MaxMatches, 20
               MaxMatches = 20
      }
     
   IfEqual, TerminatingCharacters,
      TerminatingCharacters = DftTerminatingCharacters
     
   Return
}
   
SaveScript:
; Close the tooltip if it's open
ToolTip,
IfNotEqual, LearnedWords,
{
   ; Add all the standard words to the tempwordlist
   FileRead, ParseWords, %A_ScriptDir%\Wordlist.txt
   IfEqual, LearnMode, On
   {
      LearnedwordsPos := InStr(ParseWords, "`;LEARNEDWORDS`;",true,1) ;Check for Learned Words
   } else {
            LearnedwordsPos = 0 ;force all words to be re-written if we aren't learning
         }
   IfNotEqual, LearnedwordsPos, 0
   {
      TempWordList := SubStr(ParseWords, 1, LearnedwordsPos - 1) ;Grab all non-learned words out of list
   } else {
            TempWordList := ParseWords
         }
   ParseWords =
   ; Parse the learned words and store them in a new list by count if their total count is greater than LearnCount.
   ; Prefix the word with the count and "z" for sorting
   Loop, Parse, LearnedWords, `,
   {
      SortWord := ConvertWordToAscii(A_LoopField,0)
     
      IfGreaterOrEqual, zCount%SortWord%, %LearnCount%
      {
         SortWordList .= "," . zCount%SortWord% . "z" . A_LoopField
      }
   }
   
   StringTrimLeft, SortWordList, SortWordList, 1 ;remove extra starting comma

   Sort, SortWordList, N R D, ; Sort numerically, comma delimiter

   IfNotEqual, SortWordList, ; If SortWordList exists write to the file, otherwise don't.
   {
      Loop
      {
         StringRight, LastChar, TempWordList, 1
         IF ( ( LastChar = "`r") || ( LastChar = "`n" ) )
         {
            StringTrimRight, TempWordList, TempWordList, 1
         } else {
                  Break
               }
      }
         
      IfEqual, LearnMode, On
         TempWordList .= "`r`n`;LEARNEDWORDS`;" ;only append ;LEARNEDWORDS; if we are in learning mode
      Loop, Parse, SortWordList, `,
      {
         StringTrimLeft, AppendWord, A_LoopField, InStr(A_LoopField,"z") ;Strip (Number,"z") from beginning
         TempWordList .= "`r`n" . AppendWord
      }
   
      FileDelete, %A_ScriptDir%\Temp_Wordlist.txt
      FileAppend, %TempWordList%, %A_ScriptDir%\Temp_Wordlist.txt ;Only update the file if we have learned words
      FileCopy, %A_ScriptDir%\Temp_Wordlist.txt, %A_ScriptDir%\Wordlist.txt, 1
      FileDelete, %A_ScriptDir%\Temp_Wordlist.txt
   }
}

ExitApp
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Ask for Help All times are GMT
Goto page Previous  1, 2, 3, 4, 5, 6, 7, 8, 9, 10  Next
Page 8 of 10

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group