WorkspaceCompletionSupport.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Mon, 08 Sep 2014 17:03:09 +0100
branchdelegated_gc_hg
changeset 5128 787b617a99e5
parent 5097 2d0d83f6599d
child 5129 9549b0a1dbf5
child 5220 5251ecf0c9bb
child 5694 111c73cd9b4b
permissions -rw-r--r--
Merged 52e9f87d45c8 and fcd696f9f401 (branch default)

"{ 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 contextOrNil|

    "/ a hack
    (editView topView isKindOf: DebugView) ifTrue:[
        contextOrNil := editView topView selectedContext.
    ].

    UserInformation ignoreIn:[
        DoWhatIMeanSupport 
            codeCompletionFor: editView codeAspect
            language: 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.
                    nil "/ must return nil to avoid DWIM to do it itself (for now)
            ]
    ].
    "/ 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.
    UserPreferences current appendAbbreviationsToCompletionSuggestions ifTrue:[
        (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 gray)).
                    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.
                autoSelectArg ifTrue:[
                    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).
                        ].
                    ].
                    (preselectIdx isNil and:[suggestions size == 1]) ifTrue:[
                        preselectIdx := 1.
                    ].
                ].
                preselectIdx notNil ifTrue:[
                    |pref|

                    pref := suggestions at:preselectIdx.
                    pref notNil ifTrue:[
                        "/ 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
                    ].

                (autoSelectArg 
                    and:[ (suggestions size == 1) 
                    and:[ preselectIdx == 1
                    and:[ preselectIdx ~~ indexOfSnippet ]]]) 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.9 2014-07-08 21:17:07 cg Exp $'
!

version_CVS
    ^ '$Header: /cvs/stx/stx/libwidg/WorkspaceCompletionSupport.st,v 1.9 2014-07-08 21:17:07 cg Exp $'
! !