WorkspaceCompletionSupport.st
changeset 4789 8bc0605911b2
child 4802 a25143f6e512
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WorkspaceCompletionSupport.st	Thu Sep 26 19:07:44 2013 +0200
@@ -0,0 +1,240 @@
+"{ Package: 'stx:libwidg' }"
+
+EditTextViewCompletionSupport subclass:#WorkspaceCompletionSupport
+	instanceVariableNames:''
+	classVariableNames:''
+	poolDictionaries:''
+	category:'Interface-Smalltalk'
+!
+
+!WorkspaceCompletionSupport class methodsFor:'documentation'!
+
+documentation
+"
+    A completion support using DWIM to complete code for Smalltalk (and JavaScript)
+
+    [author:]
+        Claus Gittinger
+
+    [instance variables:]
+
+    [class variables:]
+
+    [see also:]
+        DoWhatIMeanSupport
+
+"
+! !
+
+!WorkspaceCompletionSupport methodsFor:'private'!
+
+computeCompletions
+
+    |suggestions implementations actions anyFound contextOrNil|
+
+    "/ a hack
+    (editView topView isKindOf: DebugView) ifTrue:[
+        contextOrNil := editView topView selectedContext.
+    ].
+
+    UserInformation ignoreIn:[
+        anyFound := false.
+        DoWhatIMeanSupport 
+            codeCompletionForLanguage: editView editedLanguage
+            method:editView editedMethod
+            orClass:editView editedClass 
+            context:contextOrNil 
+            codeView:editView 
+            into:[:listOfSuggestions :listOfActions :titleWhenAsking |
+"/ (listOfSuggestions contains:[:l | l isEmptyOrNil]) ifTrue:[self halt].
+                    suggestions := listOfSuggestions collect:[:entry | entry isArray ifTrue:[entry first] ifFalse:[entry]].
+                    implementations := listOfSuggestions collect:[:entry | entry isArray ifTrue:[entry second] ifFalse:[nil]].                            
+                    actions := listOfActions.
+                    anyFound := true.
+                    nil "/ must return nil to avoid DWIM to do it itself (for now)
+            ]
+    ].
+    "/ anyFound ifFalse:[self halt].
+    "/ Transcript show:'suggestions: '; showCR:suggestions.
+    "/ Transcript show:'actions: '; showCR:actions.  
+    editView sensor
+        pushUserEvent:#'suggestionsArrived:implementations:actions:autoSelect:'
+        for:self
+        withArguments:{suggestions . implementations . actions . autoSelect }
+
+    "Created: / 26-09-2013 / 17:44:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+suggestionsArrived:suggestionsArg implementations:implementationsArg actions:actionsArg autoSelect:autoSelectArg
+    "the background process has generated some suggestions"
+
+    |v suggestions implementations actions suggestionOffset keyAndSnippet indexOfSnippet|
+
+    (editView sensor hasKeyPressEventFor:nil) ifTrue:[ 
+        self closeCompletionView. 
+        ^ self
+    ].
+
+    implementations := implementationsArg.
+    actions := actionsArg.
+
+    suggestions := suggestionsArg ? #().
+    suggestions size > 20 ifTrue:[ 
+        suggestions := suggestions copyTo:20.
+        implementations := implementations copyTo:20.
+        actions isArray ifTrue:[ actions := actions copyTo:20 ].
+    ].
+
+    "/ append snipplet, if any (can be easily reached via CRSR-up)
+    suggestionOffset := 0.
+    indexOfSnippet := nil.
+    (keyAndSnippet := editView findAbbreviationKeyBeforeCursor) notNil ifTrue:[
+        |abbrev sniplet i line|
+
+        abbrev := keyAndSnippet first.
+        sniplet := keyAndSnippet second.
+
+        "/ if the abbreviation is simply at the end of a longer word, ignore the abbrev.
+        line := editView lineStringBeforeCursor.
+        i := line findLast:[:ch | ch isLetterOrDigit not].
+        (i < (line size - abbrev size - 1)) ifFalse:[
+            sniplet := sniplet copyWithout:$!!.
+
+            "/ true, false and self are often found in both lists
+            (suggestions includes:sniplet) ifFalse:[   
+                suggestions isEmpty ifFalse:[ suggestions := suggestions copyWith: '-' ]. 
+                suggestions := suggestions copyWith: ( '%1 %2'
+                                        bindWith:(sniplet asStringCollection first "contractTo:25")
+                                        with: ( ('("',abbrev,'" snippet)') colorizeAllWith:Color grey)).
+                indexOfSnippet := suggestions size.
+
+                "/ change below, when reversing the order in above code
+                "/ suggestionOffset := 2.
+            ]
+        ]
+    ].
+    suggestions isEmptyOrNil ifTrue:[
+        self closeCompletionView.
+        ^ self
+    ].
+    (v := completionView) isNil ifTrue: [
+        ^ self
+    ].
+
+    v sensor
+        pushUserEvent:#value
+        for:[
+            |top idx preselectIdx performCompletion|
+
+            (v == completionView) ifTrue: [
+                top := v topView.
+
+                LastCompletions notNil ifTrue:[
+                    "/ one of the last completions in list?
+                    idx := LastCompletions findFirst:[:compl | suggestions includes:compl].
+                    idx ~~ 0 ifTrue:[
+                        preselectIdx := suggestions indexOf:(LastCompletions at:idx).
+                    ].
+                ].
+                autoSelectArg ifTrue:[
+                    (preselectIdx isNil and:[suggestions size == 1]) ifTrue:[
+                        preselectIdx := 1.
+                    ].
+                ].
+                preselectIdx notNil ifTrue:[
+                    |pref|
+
+                    pref := suggestions at:preselectIdx.
+                    "/ for now, do not move to front (action needs the index)
+                    suggestions at:preselectIdx put:(pref allBold).
+"/                    suggestions removeAtIndex:preselectIdx.                    
+"/                    suggestions addFirst:(pref allBold).
+"/                    implementations notNil ifTrue:[
+"/                        implementations removeAtIndex:preselectIdx.
+"/                        implementations addFirst:implementations.
+"/                    ]
+                ].
+
+                performCompletion :=
+                    [:selectedListIndex | 
+                        |indexInSuggestions|
+
+                        self closeCompletionView.
+                        indexInSuggestions := selectedListIndex - suggestionOffset.
+                        (selectedListIndex == indexOfSnippet) ifTrue:[
+                            "/ replace the sniplet
+                            editView sensor pushUserEvent:#expandAbbreviation for:editView
+                        ] ifFalse:[
+                            LastCompletions isNil ifTrue:[
+                                LastCompletions := OrderedCollection new.
+                            ].
+                            LastCompletions add:(suggestions at:indexInSuggestions).
+                            LastCompletions size > 200 ifTrue:[
+                                LastCompletions removeLast
+                            ].
+
+                            actions notNil ifTrue:[
+                                actions isBlock ifTrue:[
+                                    actions value:indexInSuggestions
+                                ] ifFalse:[
+                                    (actions at:indexInSuggestions) value
+                                ].
+                            ].
+                        ].
+                        "/ disabled - user has made his choice; so don't show more suggestions
+                        "/ editView sensor pushUserEvent:#updateCompletionList for:self
+                    ].
+
+                ((suggestions size == 1) and:[preselectIdx == 1]) ifTrue:[
+                    "/ do it, right here and now
+                    performCompletion value:preselectIdx.
+                ] ifFalse:[
+                    top open.
+                    v list:suggestions 
+                            expandTabs:false scanForNonStrings:false
+                            includesNonStrings:false redraw:true.
+
+                    implementations notNil ifTrue:[
+                        implementations keysAndValuesDo:[:idx :impls |
+                            |implsMenu|
+
+                            impls notEmptyOrNil ifTrue:[
+                                implsMenu := Menu new.
+                                impls do:[:each |
+                                    implsMenu addItem:(MenuItem new label:each name).
+                                ].
+                                v subMenuAt:idx put:implsMenu
+                            ].
+                        ].
+                    ].
+
+                    v enable:true.
+                    preselectIdx notNil ifTrue:[
+                        "/ very disturbing!!
+                        v selection:preselectIdx.
+                    ].
+                    v extent:completionView preferredExtentForContents.
+                    v action:performCompletion.
+
+                    (top ~~ v) ifTrue:[
+                        top resizeToFit.
+                        top bottom > v device usableHeight ifTrue:[
+                            top origin:((top origin x) @ (v device usableHeight - v height)).
+                        ].
+                        top raise.
+                    ]
+                ]
+            ]
+        ]
+! !
+
+!WorkspaceCompletionSupport class methodsFor:'documentation'!
+
+version
+    ^ '$Header: /cvs/stx/stx/libwidg/WorkspaceCompletionSupport.st,v 1.1 2013-09-26 17:07:44 vrany Exp $'
+!
+
+version_CVS
+    ^ '$Header: /cvs/stx/stx/libwidg/WorkspaceCompletionSupport.st,v 1.1 2013-09-26 17:07:44 vrany Exp $'
+! !
+