SmallSense__CompletionWindow.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Tue, 17 Sep 2013 17:18:14 +0100
changeset 88 4db839c0a78f
parent 67 020b7461b15e
child 94 9f2c651d0437
permissions -rw-r--r--
Fix in CompletionWindow>>complete Fixed completion in cases where completed text does not start with typed text. i.e, original source is `1 fa` but completing `1 exponent`.

"{ Package: 'jv:smallsense' }"

"{ NameSpace: SmallSense }"

SimpleDialog subclass:#CompletionWindow
	instanceVariableNames:'codeView result position textHolder selectionHolder
		selectionUnambigous listHolder listView'
	classVariableNames:''
	poolDictionaries:''
	category:'SmallSense-Core-Interface'
!


!CompletionWindow class methodsFor:'interface opening'!

openForView: codeView text: text items: items

    ^self new  
        initializeWithView: codeView text: text items: items;
        completeOrOpen;
        yourself

    "Created: / 19-08-2013 / 10:08:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CompletionWindow class methodsFor:'interface specs'!

windowSpec
    "This resource specification was automatically generated
     by the UIPainter of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the UIPainter may not be able to read the specification."

    "
     UIPainter new openOnClass:SmallSenseCompletionWindow andSelector:#windowSpec
     SmallSenseCompletionWindow new openInterface:#windowSpec
     SmallSenseCompletionWindow open
    "

    <resource: #canvas>

    ^ 
     #(FullSpec
        name: windowSpec
        window: 
       (WindowSpec
          label: 'SmallSenseMenu'
          name: 'SmallSenseMenu'
          min: (Point 10 10)
          bounds: (Rectangle 0 0 400 250)
          returnIsOKInDialog: false
        )
        component: 
       (SpecCollection
          collection: (
           (SelectionInListModelViewSpec
              name: 'Completions'
              layout: (LayoutFrame 0 0 0 0 0 1 0 1)
              model: selectionHolder
              hasHorizontalScrollBar: true
              hasVerticalScrollBar: true
              miniScrollerHorizontal: true
              backgroundColor: (Color 100.0 100.0 100.0)
              listModel: listHolder
              useIndex: false
              highlightMode: line
              postBuildCallback: postBuildList:
            )
           )
         
        )
      )
! !

!CompletionWindow methodsFor:'aspects'!

listHolder

    
    listHolder isNil 
        ifTrue:
            [ listHolder := ValueHolder new].
    ^ listHolder.

    "Modified: / 04-04-2011 / 21:26:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

selectionHolder
    <resource: #uiAspect>

    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept it in the browser."
    "*** (and replace this comment by something more useful ;-)"

    selectionHolder isNil ifTrue:[
        selectionHolder := ValueHolder new.
"/ if your app needs to be notified of changes, uncomment one of the lines below:
"/       selectionHolder addDependent:self.
"/       selectionHolder onChangeSend:#selectionHolderChanged to:self.
    ].
    ^ selectionHolder.
!

textHolder
    "return/create the 'textHolder' value holder (automatically generated)"

    textHolder isNil ifTrue:[
        textHolder := ValueHolder with:''.
        textHolder onChangeSend:#updateList to: self.
    ].
    ^ textHolder

    "Modified: / 26-11-2011 / 17:25:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CompletionWindow methodsFor:'change & update'!

updateList

    "Experiments with new UI"

    | t |
    result items size == 0 ifTrue:[^self].

    t := self textHolder value.
    self listHolder value isNil ifTrue:
        [self listHolder value:result items].
    [ t size > 0 ] whileTrue:[
        result items withIndexDo: [:e :i| 
            (e startsWith: t) ifTrue: [
                self selectionHolder value: e.
                ^self
            ]
        ].
        t := t allButLast.
    ]

    "
    | t list |
    t := self textHolder value.
    list := (result objectCollection select:[:e|e stringToComplete startsWith: t]).
    self listHolder value: list.
    list size = 1 ifTrue:[self selectionHolder value: list anyOne].
    "

    "Created: / 04-04-2011 / 15:43:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 26-11-2011 / 19:25:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CompletionWindow methodsFor:'events'!

complete
    | po stringToComplete stringAlreadyWritten stringToInsert textView |

    po := self selectionHolder value.
    stringToComplete := po stringToComplete.
    stringAlreadyWritten := self textHolder value.
    stringToInsert := stringToComplete copyFrom: (stringAlreadyWritten size + 1).
    textView := codeView.
    textView isCodeView2 ifTrue:[textView := textView textView].
    textView undoableDo:[
        (stringToComplete startsWith: stringAlreadyWritten) ifTrue:[
            textView insertStringAtCursor: stringToInsert.
        ] ifFalse:[
            | startCol endCol |

            endCol := textView cursorCol - 1.
            startCol := textView cursorCol - stringAlreadyWritten size.
            textView insertStringAtCursor: stringToComplete.
            textView deleteFromLine:textView cursorLine col: startCol toLine:textView cursorLine col:endCol.
            codeView cursorCol: startCol + stringToComplete size.
        ].
    ].
    codeView cursorCol: codeView cursorCol - stringToComplete size + po cursorColumnAfterComplete.

    "Created: / 26-11-2011 / 19:10:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 17-09-2013 / 17:16:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

handlesKeyPress:key inView:aView

    "/Transcript showCR: 'SmallSenseCompletionWindow handlesKeyPress: ' , key.
    ^key isCharacter or:[#(BackSpace Return Tab) includes: key]

    "Created: / 04-04-2011 / 15:52:30 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 02-08-2013 / 16:10:58 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

keyPress:key x:x y:y view:aView

    "/Transcript showCR: 'SmallSenseCompletionWindow keyPress: ' , key.

    key isCharacter ifTrue:[self keyPressCharacter: key x:x y:y. ^self].
    key == #BackSpace ifTrue:[self keyPressBackSpaceX:x y:y.^self].
    key == #Return ifTrue:[self keyPressReturnX:x y:y.^self].
    key == #Tab ifTrue:[self keyPressTabX:x y:y.^self].

    "Created: / 04-04-2011 / 15:51:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 02-08-2013 / 16:11:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

keyPressBackSpaceX:x y:y

    | text |
    codeView textView keyPress:#BackSpace x:x y:y.
    text := self textHolder value.
    text size == 0 ifTrue:[self close. ^self].
    self textHolder value: (text copyTo: text size - 1).

    "Created: / 04-04-2011 / 16:56:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 08-04-2011 / 22:49:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

keyPressCharacter: char x:x y:y

    | text |
    char isSeparator ifTrue:[
        self close
    ] ifFalse:[
        text := self textHolder value.
        self textHolder value: (text , char).
    ].
    codeView textView keyPress:char x:x y:y.

    "Created: / 04-04-2011 / 16:56:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 08-08-2013 / 13:32:53 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

keyPressReturnX:x y:y

    self selectionHolder value isNil ifTrue:[self close.^self].
    self complete.
    self close.

    "Created: / 04-04-2011 / 16:57:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (format): / 26-11-2011 / 19:11:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

keyPressTabX:x y:y

    | stringsToComplete stringToComplete len i stringAlreadyWritten stringToInsert textView |

    stringAlreadyWritten := self textHolder value.

    stringsToComplete := listHolder value collect:[:po | po stringToComplete ].
    stringsToComplete := stringsToComplete select:[:s | s startsWith: stringAlreadyWritten ].
    stringsToComplete isEmpty ifTrue:[
        listView flash.
        ^ self.
    ].
    len := stringsToComplete inject: SmallInteger maxVal into:[:min :s | min min: s size ].
    len == stringAlreadyWritten size ifTrue:[
        listView flash.
        ^ self.
    ].
    i := stringAlreadyWritten size + 1.
    [ stringToComplete isNil ] whileTrue:[
        | c |

        i > len ifTrue:[
            stringToComplete := stringsToComplete first copyTo: len        
        ] ifFalse:[
            c := stringsToComplete first at: i. 
            (stringsToComplete anySatisfy:[:s|(s at: i) ~~ c ]) ifTrue:[
                i > (stringAlreadyWritten size + 1) ifTrue:[
                    stringToComplete := stringsToComplete first copyTo: i - 1
                ] ifFalse:[
                    listView flash.
                    ^ self.
                ]
            ] ifFalse:[
                i := i + 1.
            ]
        ].
    ].

    stringToInsert := stringToComplete copyFrom: (stringAlreadyWritten size + 1).
    textView := codeView.
    textView isCodeView2 ifTrue:[textView := textView textView].
    textView undoableDo:[
        (stringToComplete startsWith: stringAlreadyWritten) ifTrue:[
            textView insertStringAtCursor: stringToInsert.
        ] ifFalse:[
            textView cursorCol: textView cursorCol - stringAlreadyWritten size.
            textView deleteFromLine:textView cursorLine col: textView cursorCol toLine:textView cursorLine col:textView cursorCol + stringAlreadyWritten size.
            textView insertStringAtCursor: stringToComplete.
        ].
    ].
    self textHolder setValue: stringToComplete.
    stringsToComplete size == 1 ifTrue:[
        self close.
    ]
    "/codeView cursorCol: codeView cursorCol.

    "Created: / 02-08-2013 / 16:11:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 05-08-2013 / 18:34:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CompletionWindow methodsFor:'hooks'!

postBuildList: aView

    listView := aView scrolledView.
    listView
        background: codeView background;
        font: codeView font;
        delegate: self.

    "Created: / 03-04-2011 / 10:39:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 02-08-2013 / 16:20:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

postBuildWith: builder

    builder window 
        windowGroup: codeView windowGroup;
        bePopUpView; 
        beSlave.
    builder window sensor setCtrlDown: false.

    "Created: / 03-04-2011 / 10:45:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 02-06-2011 / 21:36:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CompletionWindow methodsFor:'initialization'!

initializeWithView: view text: text items: items
    "Initializes the completion window for given context.
     - `view` is the CodeView2 in which to complete
     - `text` is partially typed text (part of variable name
        or selector)
     - items is a set of items to complete (a set of SmallSense*PO)
    "

    codeView := view.
    result := items.
    result notEmptyOrNil ifTrue:[
        self textHolder value: text.
        self updateList
    ] ifFalse:[
        codeView isCodeView2 ifTrue:[
            codeView textView flash
        ] ifFalse:[
            codeView flash.
        ].
    ]

    "Created: / 19-08-2013 / 10:01:52 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 19-08-2013 / 15:15:04 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CompletionWindow methodsFor:'interface opening'!

completeOrOpen

    | text matching|

    result isEmptyOrNil ifTrue:[^self].

    (text := textHolder value) notEmptyOrNil ifTrue:[
        matching := result items select:[:item|item startsWith: text].
        matching size == 1 ifTrue:[
"/            v := matching anElement.
"/            (v := selectionHolder value) notNil ifTrue:[
"/                (v isSmallSenseVariablePO or:[v isSmallSenseClassPO]) ifTrue:[
                    self complete.
                    ^self.
"/                ]
"/            ].        
       ].
    ].

    self openModal.

    "Created: / 26-11-2011 / 19:08:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 19-08-2013 / 15:15:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

openModal

    | p textView x y |

    textView := codeView textView.
    x := (textView xOfCol:textView cursorCol  inVisibleLine:textView cursorLine)
            - 16"icon" - (textView widthOfString: self textHolder value) - 5"magic constant".
    y := textView yOfCursor + textView font maxHeight + 3.
    p := (textView originRelativeTo: nil) + (x @ y).

    self openInterfaceModal:#windowSpec at:p

    "Created: / 04-04-2011 / 21:12:04 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CompletionWindow methodsFor:'opening'!

openWindowModalAt:aPoint
    "open the window as a modal dialog
     - assumes that the builder has already setup the interface."

    | windowExtent screenExtent|

    windowExtent := builder window extent copy.
    screenExtent := Screen current monitorBoundsAt: aPoint.
    (screenExtent height) < (aPoint y + windowExtent y) ifTrue:[
        aPoint y: (aPoint y - windowExtent y - codeView textView font maxHeight - 5).
    ].
    builder openDialogAt:aPoint withExtent: windowExtent.

    "Created: / 01-07-2013 / 12:22:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 24-07-2013 / 16:41:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CompletionWindow class methodsFor:'documentation'!

version_HG

    ^ '$Changeset: <not expanded> $'
!

version_SVN
    ^ '$Id: SmallSenseCompletionWindow.st 7911 2012-02-22 09:55:48Z vranyj1 $'
! !