Tools__CodeHighlightingService.st
author Claus Gittinger <cg@exept.de>
Fri, 01 Jul 2011 15:20:16 +0200
changeset 9980 cf0d7a8b54e9
child 10077 d2b07a20c425
permissions -rw-r--r--
initial checkin

"
 COPYRIGHT (c) 2006 by eXept Software AG
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libtool' }"

"{ NameSpace: Tools }"

CodeViewService subclass:#CodeHighlightingService
	instanceVariableNames:'worker workerRunning'
	classVariableNames:''
	poolDictionaries:''
	category:'Interface-CodeView'
!

!CodeHighlightingService class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2006 by eXept Software AG
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
! !

!CodeHighlightingService class methodsFor:'accessing'!

label

    "Answers short label - for UI"

    ^'Syntax Highlighting'

    "Created: / 07-03-2010 / 14:00:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CodeHighlightingService methodsFor:'change & update'!

update: aspect with: param from: sender

    sender == codeView textView modifiedChannel ifTrue:[^self codeChanged: false].
    sender == codeView textView model ifTrue:[^self codeChanged: true].
    sender == codeView languageHolder ifTrue:[^self codeChanged: true].
    sender == codeView classHolder ifTrue:[^self codeChanged: true].

    super update: aspect with: param from: sender

    "Created: / 06-03-2010 / 19:38:07 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CodeHighlightingService methodsFor:'private'!

codeChanged: force

    (force or:[codeView textView modified]) ifTrue:
        [self startSyntaxHighlightProcess].

    "Modified: / 06-03-2010 / 19:32:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

setHighlightedCode:newCode elements: elements
    "the background synhighlighter has generated new colored text,
     with highlighted syntax.
     If there have been no modifications in the meantime, install it."

    |firstShown lastShown cursorWasOn anyChange newLines l replaceAction textView list|

    textView := codeView textView.
    textView modified ifTrue:[
        "/ new input arrived in the meantime

        ^ self
    ].
    worker notNil ifTrue:[
        "/ another coloring process has already been started.
        "/ ignore this (leftover) code.

        ^ self
    ].
    firstShown := textView firstLineShown.
    lastShown := textView lastLineShown.
    replaceAction := [:lNr :line |
            |oldLine|

            oldLine := list at:lNr ifAbsent:nil.
            oldLine notNil ifTrue:[
                line notNil ifTrue:[
                    "/ this check is needed - there is a race
                    "/ when the text is converted. This detects the
                    "/ resulting error.
                    "/ Certainly a kludge.

                    oldLine string = line string ifTrue:[
                        oldLine emphasis ~= line emphasis ifTrue:[
                            textView modifiedChannel removeDependent:self.
                            list at:lNr put:line.
                            textView modifiedChannel addDependent:self.
                            (lNr between:firstShown and:lastShown) ifTrue:[
                                anyChange ifFalse:[
                                    anyChange := true.
                                    cursorWasOn := textView hideCursor
                                ].
                                textView redrawLine:lNr
                            ]
                        ]
                    ]
                ]
            ]
        ].
    anyChange := false.
    newLines := newCode asStringCollection.
    list := textView list.
    list isNil ifTrue:[
        textView list:newLines.
    ] ifFalse:[
        "/ the cursor line first - thats where your eyes are ...
        (l := textView cursorLine) notNil ifTrue:[
            l <= newLines size ifTrue:[
                replaceAction value:l value:(newLines at:l)
            ]
        ].
        newLines keysAndValuesDo:replaceAction.
        anyChange ifTrue:[
            cursorWasOn ifTrue:[
                textView showCursor
            ]
        ]
    ].
    codeView syntaxElements: elements

    "Modified: / 09-10-2006 / 11:50:17 / cg"
    "Created: / 14-02-2010 / 16:10:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 06-03-2010 / 19:58:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

showInfo: aString

    codeView showInfo: aString

    "Created: / 06-03-2010 / 19:34:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

startSyntaxHighlightProcess
    "start a background process, which does the syntax coloring.
     When it finishes, it pushes a user event to show the new text in the codeView.
     (This is done as an event to synchronize the coloring with modifications
      done to the text - the colored text will discarded, if there were
      any new modifications in the meanwhile)"

    |dontDoIt oldCodeList highlighterClass prio textView|

    textView := codeView textView.

    highlighterClass := self syntaxHighlighterClass.

    dontDoIt := highlighterClass isNil.
    "dontDoIt := dontDoIt
                or:[self doSyntaxColoring value ~~ true
                or:[(self doImmediateSyntaxColoring) value ~~ true]]."
    dontDoIt ifTrue:[^self].




    "/ this clobbers the codeViews modified state; therefore, we have to remember
    "/ this info somewhere ...
    textView modified ifTrue:[
        codeView modifiedChannel value:true
    ].
    textView modifiedChannel setValue:false.

    worker notNil ifTrue:[
        workerRunning ~~ true ifTrue:[
            "/ process already created, but did not get a change to start yet;
            ^ self
        ].
        self stopSyntaxHighlightProcess
    ].
    prio := Processor userBackgroundPriority - 1.
    textView shown ifFalse:[
        prio := prio - 1 max:1
    ].
    worker := [
                |oldCode newCode elements cls sensor |
                [
                    workerRunning := true.
                    codeView syntaxElements: nil.
                    codeView syntaxElementSelection: nil.
                    cls := codeView classHolder value.
                    (cls notNil and:[cls isObsolete]) ifTrue:[
                        cls isMeta ifTrue:[
                            cls := (Smalltalk at:cls theNonMetaclass name) class
                        ] ifFalse:[
                            cls := Smalltalk at:cls name
                        ].
                    ].
                    textView modified ifFalse:[
                        oldCodeList := textView list copy.
                        textView modified ifFalse:[
                            oldCodeList isNil ifFalse:[
                                oldCode := oldCodeList asStringWithoutEmphasis.
                                textView modified ifFalse:[
                                    Screen currentScreenQuerySignal answer:codeView device
                                    do:[
                                        Parser::ParseError handle:[:ex |
                                            |errMsg|

                                            errMsg := ex description asStringCollection first asString.

                                            "/ Transcript topView raiseDeiconified.
                                            "/ Transcript showCR:'ParseError: ', ex description.
"/ self halt.
                                            self showInfo:(errMsg colorizeAllWith:Color red).
                                            newCode := nil.
                                        ] do:[
                                            elements := SortedCollection new.
                                            codeView codeAspect == #method ifTrue:[
                                                newCode := highlighterClass formatMethod:oldCode in:cls using: nil elementsInto: elements.
                                            ] ifFalse:[
                                                codeView codeAspect == #expression ifTrue:[
                                                    newCode := highlighterClass formatExpression:oldCode in:cls elementsInto: elements.
                                                ] ifFalse:[
                                                    codeView codeAspect == #classDefinition ifTrue:[                                            
                                                        newCode := highlighterClass formatClassDefinition:oldCode in:cls elementsInto: elements.
                                                    ]
                                                ].
                                            ].
                                        ]
                                    ].
                                    newCode notNil ifTrue:[
                                        textView modified ifFalse:[
                                            newCode := newCode asStringCollection.
                                            textView modified ifFalse:[
                                                worker := nil.
                                                (textView := codeView textView) notNil ifTrue:[
                                                    "/ must add this event - and not been interrupted
                                                    "/ by any arriving key-event.
                                                    self showInfo:nil.
                                                    codeView sensor
                                                        pushUserEvent:#setHighlightedCode:elements:
                                                        for:self
                                                        withArguments:(Array with:newCode with: elements).
                                                    "/self delayedUpdateBufferLabelWithCheckIfModified
                                                ]
                                            ]
                                        ]
                                    ]
                                ]
                            ]
                        ]
                    ]
                ] ensure:[
                    workerRunning := false.
                    worker := nil
                ]
            ] forkAt:prio

    "Modified: / 22-08-2006 / 14:27:09 / cg"
    "Modified: / 10-04-2011 / 18:21:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

stopSyntaxHighlightProcess
    "stop any syntax coloring background process."

    |p|

    (p := worker) ifNil:[^self].
    worker := nil.
    p terminate.
    "/ raise its prio to make it terminate quickly
    p priority:(Processor userSchedulingPriority + 1)

    "Modified: / 03-09-2010 / 22:27:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

syntaxHighlighterClass

    | lang cls |
    cls := (lang := codeView languageHolder value)
                ifNil:[nil]
                ifNotNil:[lang syntaxHighlighterClass].
    "Ugly hack because I don't want to branch libcomp :-)"
    cls == SyntaxHighlighter ifTrue:[cls := SyntaxHighlighter2].
    ^cls

    "Created: / 14-02-2010 / 12:39:15 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 06-03-2010 / 19:39:05 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!CodeHighlightingService class methodsFor:'documentation'!

version_CVS
    ^ '$Header: /cvs/stx/stx/libtool/Tools__CodeHighlightingService.st,v 1.1 2011-07-01 13:20:16 cg Exp $'
!

version_SVN
    ^ '§Id: Tools__CodeHighlightingService.st 7715 2011-04-10 16:32:58Z vranyj1 §'
! !