Custom Scintilla lexer

Share your advanced PureBasic knowledge/code with the community.
MrMat
Enthusiast
Enthusiast
Posts: 762
Joined: Sun Sep 05, 2004 6:27 am
Location: England

Custom Scintilla lexer

Post by MrMat »

Code updated for 6.03+

This is an example of writing a simple custom lexer (with highlighting/code folding) for PB 4 in Windows using Scintilla. Prototypes are used to call the Scintilla API directly:

Code: Select all

#SCI_GETCHARAT = 2007
#SCI_GETCURRENTPOS = 2008
#SCI_GETANCHOR = 2009
#SCI_GOTOLINE = 2024
#SCI_GOTOPOS = 2025
#SCI_SETANCHOR = 2026
#SCI_GETENDSTYLED = 2028
#SCI_STARTSTYLING = 2032
#SCI_SETSTYLING = 2033
#SCI_MARKERDEFINE = 2040
#SCI_MARKERSETFORE = 2041
#SCI_MARKERSETBACK = 2042
#SCI_STYLESETSIZE = 2055
#SCI_STYLESETFONT = 2056
#SCI_STYLECLEARALL = 2050
#SCI_STYLESETFORE = 2051
#SCI_STYLESETBOLD = 2053
#SCI_STYLESETITALIC = 2054
#SCI_GETLENGTH = 2006
#SCI_SETSEL = 2160
#SCI_LINEFROMPOSITION = 2166
#SCI_POSITIONFROMLINE = 2167
#SCI_SETTEXT = 2181
#SCI_GETTEXT = 2182
#SCI_GETDIRECTPOINTER = 2185
#SCI_SETCARETLINEVISIBLE = 2096
#SCI_SETCARETLINEBACK = 2098
#SCI_SETFOLDLEVEL = 2222
#SCI_GETFOLDLEVEL = 2223
#SCI_SETMARGINTYPEN = 2240
#SCI_APPENDTEXT = 2282
#SCI_TOGGLEFOLD = 2231
#SCI_SETMARGINWIDTHN = 2242
#SCI_SETMARGINMASKN = 2244
#SCI_SETMARGINSENSITIVEN = 2246
#SCI_SEARCHANCHOR = 2366
#SCI_SEARCHNEXT = 2367
#SCI_SETLEXER = 4001

#SC_MARGIN_NUMBER = 1
#SC_FOLDLEVELBASE = $400
#SC_FOLDLEVELHEADERFLAG = $2000
#SC_MASK_FOLDERS = $FE000000

#STYLE_DEFAULT = 32

#SCLEX_CONTAINER = 0
#SCLEX_PUREBASIC = 67

#SC_MARK_CIRCLE = 0
#SC_MARK_ROUNDRECT = 1
#SC_MARK_ARROW = 2
#SC_MARK_SMALLRECT = 3
#SC_MARK_SHORTARROW = 4
#SC_MARK_EMPTY = 5
#SC_MARK_ARROWDOWN = 6
#SC_MARK_MINUS = 7
#SC_MARK_PLUS = 8
#SC_MARK_VLINE = 9
#SC_MARK_LCORNER = 10
#SC_MARK_TCORNER = 11
#SC_MARK_BOXPLUS = 12
#SC_MARK_BOXPLUSCONNECTED = 13
#SC_MARK_BOXMINUS = 14
#SC_MARK_BOXMINUSCONNECTED = 15
#SC_MARK_LCORNERCURVE = 16
#SC_MARK_TCORNERCURVE = 17
#SC_MARK_CIRCLEPLUS = 18
#SC_MARK_CIRCLEPLUSCONNECTED = 19
#SC_MARK_CIRCLEMINUS = 20
#SC_MARK_CIRCLEMINUSCONNECTED = 21
#SC_MARK_BACKGROUND = 22
#SC_MARK_DOTDOTDOT = 23
#SC_MARK_ARROWS = 24
#SC_MARK_PIXMAP = 25
#SC_MARK_FULLRECT = 26
#SC_MARK_CHARACTER = 10000

#SC_MARKNUM_FOLDEREND = 25
#SC_MARKNUM_FOLDEROPENMID = 26
#SC_MARKNUM_FOLDERMIDTAIL = 27
#SC_MARKNUM_FOLDERTAIL = 28
#SC_MARKNUM_FOLDERSUB = 29
#SC_MARKNUM_FOLDER = 30
#SC_MARKNUM_FOLDEROPEN = 31

#SCN_STYLENEEDED = 2000
#SCN_CHARADDED = 2001
#SCN_SAVEPOINTREACHED = 2002
#SCN_SAVEPOINTLEFT = 2003
#SCN_MODIFYATTEMPTRO = 2004
#SCN_KEY = 2005
#SCN_DOUBLECLICK = 2006
#SCN_UPDATEUI = 2007
#SCN_MODIFIED = 2008
#SCN_MACRORECORD = 2009
#SCN_MARGINCLICK = 2010
#SCN_NEEDSHOWN = 2011
#SCN_PAINTED = 2013
#SCN_USERLISTSELECTION = 2014
#SCN_URIDROPPED = 2015
#SCN_DWELLSTART = 2016
#SCN_DWELLEND = 2017
#SCN_ZOOM = 2018
#SCN_HOTSPOTCLICK = 2019
#SCN_HOTSPOTDOUBLECLICK = 2020
#SCN_CALLTIPCLICK = 2021
#SCN_AUTOCSELECTION = 2022

#INDICS_MASK = $E0

Enumeration 0
    #LexerState_Space
    #LexerState_Comment
    #LexerState_Keyword
    #LexerState_FoldKeyword
EndEnumeration

Prototype ScintillaDirect(sciptr, msg, param1 = 0, param2 = 0)
Global Scintilla.ScintillaDirect, sciptr
Global scihWnd, oldproc

Procedure LoadFile()
    filename.s = OpenFileRequester("Open a file...", "", "PureBasic (*.pb)|*.pb|All files (*.*)|*.*", 0)
    If filename <> ""
        file = ReadFile(#PB_Any, filename)
        If file <> 0
            len = Lof(file)
            *mem = AllocateMemory(len)
            If *mem
                ReadData(file, *mem, len)
                Scintilla(sciptr, #SCI_SETTEXT, 0, *mem)
                FreeMemory(*mem)
            EndIf
            CloseFile(file)
        EndIf
    EndIf
EndProcedure

Procedure SaveFile()
    filename.s = SaveFileRequester("Save a file...", "", "PureBasic (*.pb)|*.pb|All files (*.*)|*.*", 0)
    If filename <> ""
        file = CreateFile(#PB_Any, filename)
        If file <> 0
            len = Scintilla(sciptr, #SCI_GETLENGTH) + 1
            *mem = AllocateMemory(len)
            If *mem
                Scintilla(sciptr, #SCI_GETTEXT, len, *mem)
                WriteData(file, *mem, len)
                FreeMemory(*mem)
            EndIf
            CloseFile(file)
        EndIf
    EndIf
EndProcedure

Procedure FindText()
    text.s = InputRequester("Scintilla test", "Enter text to search for", "")
    If text <> ""
        current = Scintilla(sciptr, #SCI_GETCURRENTPOS)
        Scintilla(sciptr, #SCI_SETANCHOR, current)
        Scintilla(sciptr, #SCI_SEARCHANCHOR)
        start = Scintilla(sciptr, #SCI_SEARCHNEXT, 0, @text)
        If pos <> -1
            anchor = Scintilla(sciptr, #SCI_GETANCHOR)
            Scintilla(sciptr, #SCI_SETSEL, start, anchor)
        EndIf
    EndIf
    SetFocus_(scihWnd)
EndProcedure

Procedure GotoLine()
    line.s = InputRequester("Scintilla test", "Enter line to go to", "1")
    lineno = Val(line)
    If lineno > 0
        Scintilla(sciptr, #SCI_GOTOLINE, lineno - 1)
    EndIf
    SetFocus_(scihWnd)
EndProcedure

Procedure ToggleCurrentFold()
    pos = Scintilla(sciptr, #SCI_GETCURRENTPOS)
    line = Scintilla(sciptr, #SCI_LINEFROMPOSITION, pos)
    Scintilla(sciptr, #SCI_TOGGLEFOLD, line)
EndProcedure

Procedure Highlight(sciptr, startpos, endpos)
    Debug(sciptr)
    If startpos = -1
        endstyled = Scintilla(sciptr, #SCI_GETENDSTYLED)
        linenumber = Scintilla(sciptr, #SCI_LINEFROMPOSITION, endstyled)
    Else
        linenumber = Scintilla(sciptr, #SCI_LINEFROMPOSITION, startpos)
    EndIf
   
    If linenumber = 0
        level = #SC_FOLDLEVELBASE
    Else
        linenumber - 1
        level = Scintilla(sciptr, #SCI_GETFOLDLEVEL, linenumber) & ~ #SC_FOLDLEVELHEADERFLAG
    EndIf
   
    thislevel = level
    nextlevel = level
   
    currentpos = Scintilla(sciptr, #SCI_POSITIONFROMLINE, linenumber)
    Scintilla(sciptr, #SCI_STARTSTYLING, currentpos, $1f | #INDICS_MASK)
    state = #LexerState_Space
    startkeyword = currentpos
    keyword.s = ""
   
    While currentpos <= endpos
        oldstate = state
        char = Scintilla(sciptr, #SCI_GETCHARAT, currentpos)
        If char = ';'
            state = #LexerState_Comment
        ElseIf char = 10 Or char = 13
            state = #LexerState_Space
        ElseIf state <> #LexerState_Comment
            If char = 9 Or char = ' ' Or char = '.'
                state = #LexerState_Space
            Else
                state = #LexerState_Keyword
                keyword + Chr(char)
            EndIf
        EndIf
        If oldstate <> state Or currentpos = endpos
            If oldstate = #LexerState_Keyword
                lkeyword.s = LCase(keyword)
                If lkeyword = "procedure"
                    thislevel | #SC_FOLDLEVELHEADERFLAG
                    nextlevel + 1
                    oldstate = #LexerState_FoldKeyword
                ElseIf lkeyword = "endprocedure"
                    nextlevel - 1
                    If nextlevel < #SC_FOLDLEVELBASE
                        nextlevel = #SC_FOLDLEVELBASE
                    EndIf
                    oldstate = #LexerState_FoldKeyword
                EndIf
                keyword = ""
            EndIf
            Scintilla(sciptr, #SCI_SETSTYLING, currentpos - startkeyword, oldstate)
            startkeyword = currentpos
        EndIf
       
        If char = 10 Or currentpos = endpos
            Scintilla(sciptr, #SCI_SETFOLDLEVEL, linenumber, thislevel)
            thislevel = nextlevel
            linenumber + 1
        EndIf
       
        currentpos + 1
    Wend
EndProcedure

Procedure ScintillaWindowCallback(hWnd, Msg, wParam, lParam)
    result = 0
    Select Msg
        Case #WM_NOTIFY
            *lpnmhdr.NMHDR = lParam
            *notify.SCNotification = lParam
            Select *lpnmhdr\code
                Case #SCN_STYLENEEDED
                    Highlight(sciptr, -1, *notify\position)
                Case #SCN_MARGINCLICK
                    modifiers = *notify\modifiers
                    position = *notify\position
                    margin = *notify\margin
                    linenumber = Scintilla(sciptr, #SCI_LINEFROMPOSITION, position)
                    Select margin
                        Case 2
                            Scintilla(sciptr, #SCI_TOGGLEFOLD, linenumber)
                    EndSelect
            EndSelect
        Default
            result = CallWindowProc_(oldproc, hWnd, Msg, wParam, lParam)
    EndSelect
    ProcedureReturn result
EndProcedure

file.s = "Scintilla.dll"

scihmod = OpenLibrary(#PB_Any, file)
Debug scihmod
If scihmod = 0
    MessageRequester("Error", "Cannot open "+file)
    End
EndIf

OpenWindow(0, 0, 0, 600, 400, "Scintilla example", #PB_Window_SystemMenu)

CreateMenu(0, WindowID(0))
MenuTitle("File")
MenuItem(1, "Open...")
MenuItem(2, "Save As...")
MenuItem(3, "Quit")
MenuTitle("Edit")
MenuItem(4, "Find...")
MenuItem(5, "Goto...")
MenuItem(6, "Toggle current fold")

GetClientRect_(WindowID(0), rect.RECT)
scihWnd = CreateWindowEx_(#WS_EX_CLIENTEDGE, "Scintilla", "", #WS_CHILD | #WS_VISIBLE | #WS_TABSTOP, 0, 0, rect\right, rect\bottom, WindowID(0), 0, GetModuleHandle_(0), 0)

Scintilla = GetFunction(scihmod, "Scintilla_DirectFunction")
sciptr = SendMessage_(scihWnd, #SCI_GETDIRECTPOINTER, 0, 0)

oldproc = SetWindowLongPtr_(WindowID(0), #GWL_WNDPROC, @ScintillaWindowCallback())

; Set default font
Scintilla(sciptr, #SCI_STYLESETFONT, #STYLE_DEFAULT, UTF8("Courier New"))
Scintilla(sciptr, #SCI_STYLESETSIZE, #STYLE_DEFAULT, 16)
Scintilla(sciptr, #SCI_STYLECLEARALL)

; Set caret line colour
Scintilla(sciptr, #SCI_SETCARETLINEBACK, $eeeeff)
Scintilla(sciptr, #SCI_SETCARETLINEVISIBLE, #True)

; Set styles for custom lexer
Scintilla(sciptr, #SCI_STYLESETFORE, #LexerState_Comment, $bb00)
Scintilla(sciptr, #SCI_STYLESETITALIC, #LexerState_Comment, 1)
Scintilla(sciptr, #SCI_STYLESETFORE, #LexerState_Keyword, 0)
Scintilla(sciptr, #SCI_STYLESETFORE, #LexerState_FoldKeyword, $ff)

; Margins
Scintilla(sciptr, #SCI_SETMARGINTYPEN, 0, #SC_MARGIN_NUMBER)
Scintilla(sciptr, #SCI_SETMARGINMASKN, 2, #SC_MASK_FOLDERS)
Scintilla(sciptr, #SCI_SETMARGINWIDTHN, 0, 50)
Scintilla(sciptr, #SCI_SETMARGINWIDTHN, 2, 20)
Scintilla(sciptr, #SCI_SETMARGINSENSITIVEN, 2, #True)

; Choose folding icons
Scintilla(sciptr, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDEROPEN, #SC_MARK_CIRCLEMINUS)
Scintilla(sciptr, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDER, #SC_MARK_CIRCLEPLUS)
Scintilla(sciptr, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDERSUB, #SC_MARK_VLINE)
Scintilla(sciptr, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDERTAIL, #SC_MARK_LCORNERCURVE)
Scintilla(sciptr, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDEREND, #SC_MARK_CIRCLEPLUSCONNECTED)
Scintilla(sciptr, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDEROPENMID, #SC_MARK_CIRCLEMINUSCONNECTED)
Scintilla(sciptr, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDERMIDTAIL, #SC_MARK_TCORNERCURVE)

; Choose folding icon colours
Scintilla(sciptr, #SCI_MARKERSETFORE, #SC_MARKNUM_FOLDER, $FFFFFF)
Scintilla(sciptr, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDER, 0)
Scintilla(sciptr, #SCI_MARKERSETFORE, #SC_MARKNUM_FOLDEROPEN, $FFFFFF)
Scintilla(sciptr, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDEROPEN, 0)
Scintilla(sciptr, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDEROPENMID, 0)
Scintilla(sciptr, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDERSUB, 0)
Scintilla(sciptr, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDERTAIL, 0)
Scintilla(sciptr, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDERMIDTAIL, 0)

; Set some sample text
text.s = "; A custom Scintilla lexer example" + #CRLF$
text + "Procedure hello()" + #CRLF$
text + "  Debug(" + Chr(34) + "Woo" + Chr(34) + ")" + #CRLF$
text + "EndProcedure"
Scintilla(sciptr, #SCI_SETTEXT, 0, UTF8(text))

RemoveKeyboardShortcut(0, #PB_Shortcut_Tab)
RemoveKeyboardShortcut(0, #PB_Shortcut_Tab | #PB_Shortcut_Shift)

quit = #False
Repeat
    event = WaitWindowEvent()
    Select event
        Case #PB_Event_CloseWindow
            quit = #True
        Case #PB_Event_Menu
            Select EventMenu()
                Case 1 : LoadFile()
                Case 2 : SaveFile()
                Case 3 : quit = #True
                Case 4 : FindText()
                Case 5 : GotoLine()
                Case 6 : ToggleCurrentFold()
            EndSelect
    EndSelect
Until quit

DestroyWindow_(scihWnd)
CloseLibrary(scihmod)
There's probably a few different ways of going about writing a lexer but this way works fine. Basically we tell Scintilla that we'll do the styling by calling #SCI_SETLEXER with #SCLEX_CONTAINER. Then Scintilla requests a section of code is styled by sending a #WM_NOTIFY message to the parent window, with a #SCN_STYLENEEDED notification. The Highlight procedure then parses the section of code that needs styling. By looking at each character in turn, a variable is updated that holds the current state at that position (by state we mean if a keyword, comment, etc. is being parsed). When the state changes, that part of the code is styled appropriately. If certain keywords are parsed then they change the folding level. Here's a screenshot:
Image
Obviously there's a lot more involved in writing a full lexer but that is the basic idea :) The Scintilla documentation is a must read for general Scintilla help. SciLexer.dll is required to run the code above (get it with the Windows Scite binary).
Mat
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Magic. :D
@}--`--,-- A rose by any other name ..
thefool
Always Here
Always Here
Posts: 5875
Joined: Sat Aug 30, 2003 5:58 pm
Location: Denmark

Post by thefool »

Thanks again :)
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

absolutely totally and fully completely fabeltastic! i just swallowed it wholesale :-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
MrMat
Enthusiast
Enthusiast
Posts: 762
Joined: Sun Sep 05, 2004 6:27 am
Location: England

Post by MrMat »

Thanks for the kind words :)
Mat
mdp
Enthusiast
Enthusiast
Posts: 115
Joined: Mon Apr 18, 2005 8:28 pm

Post by mdp »

Thank you!
Most useful indeed :D
User avatar
eddy
Addict
Addict
Posts: 1479
Joined: Mon May 26, 2003 3:07 pm
Location: Nantes

Post by eddy »

interesting :)

It will be the default lexer example of my lib.
Imagewin10 x64 5.72 | IDE | PB plugin | Tools | Sprite | JSON | visual tool
dracflamloc
Addict
Addict
Posts: 1648
Joined: Mon Sep 20, 2004 3:52 pm
Contact:

Post by dracflamloc »

Thank you this was priceless to me for a little dracscript editor
BriceManuel
Enthusiast
Enthusiast
Posts: 195
Joined: Thu Nov 29, 2007 8:23 am

Post by BriceManuel »

How did I miss this little gem?
dracflamloc
Addict
Addict
Posts: 1648
Joined: Mon Sep 20, 2004 3:52 pm
Contact:

Post by dracflamloc »

Here is a modified program tailored to DracScript, for anyone whos interested.

Code: Select all

#DS_EDITOR_KEYWORDS = " if else elseif endif exit free while wend goto label "
#DS_EDITOR_OPENFOLD = " while if "
#DS_EDITOR_CLOSEFOLD = " wend endif "
#DS_EDITOR_ENDCONSTANT = "+=-*/%$ !<>|\/.,()"

Enumeration 0
    #LexerState_Space
    #LexerState_Comment
    #LexerState_NonKeyword
    #LexerState_Keyword
    #LexerState_FoldKeyword
    #LexerState_Constant
    #LexerState_String
EndEnumeration

Procedure.l Scintilla(sciptr.l, p1.l, p2.l=0, p3.l=0)
  ProcedureReturn ScintillaSendMessage(sciptr,p1,p2,p3)
EndProcedure

Procedure Highlight(sciptr.l, startpos.l, endpos.l)
    Debug(sciptr)
    If startpos = -1
        endstyled.l = Scintilla(sciptr, #SCI_GETENDSTYLED)
        linenumber.l = Scintilla(sciptr, #SCI_LINEFROMPOSITION, endstyled)
    Else
        linenumber = Scintilla(sciptr, #SCI_LINEFROMPOSITION, startpos)
    EndIf
   
    If linenumber = 0
        level = #SC_FOLDLEVELBASE
    Else
        linenumber - 1
        level = Scintilla(sciptr, #SCI_GETFOLDLEVEL, linenumber) & ~ #SC_FOLDLEVELHEADERFLAG
    EndIf
   
    thislevel.l = level
    nextlevel.l = level
   
    currentpos.l = Scintilla(sciptr, #SCI_POSITIONFROMLINE, linenumber)
    Scintilla(sciptr, #SCI_STARTSTYLING, currentpos, $1f | #INDICS_MASK)
    state = #LexerState_Space
    startkeyword = currentpos
    keyword.s = ""
   
    qpos.l = -1 ;quote pos
    While currentpos <= endpos
        oldstate = state
        lastchar.l = char.l        
        char.l = Scintilla(sciptr, #SCI_GETCHARAT, currentpos)
        If char = ';'
            state = #LexerState_Comment
        ElseIf char = '"' And state<>#LexerState_String
            state = #LexerState_String
            qpos = currentpos
        ElseIf state = #LexerState_String
            If (qpos<>currentpos-1 And lastchar = '"') Or char = 10 Or char = 13
              state=#LexerState_Space
              qpos=-1
            EndIf         
        ElseIf char = 10 Or char = 13
            state = #LexerState_Space
        ElseIf state <> #LexerState_Comment
            If state=#LexerState_Constant
              If FindString(#DS_EDITOR_ENDCONSTANT,Chr(char),1) Or char=10 Or char=13 Or char=9
                state=#LexerState_Space
              EndIf 
            Else             
              If char='#'
                  state = #LexerState_Constant                
              ElseIf char = 9 Or char = ' ' Or char = '.'
                  state = #LexerState_Space
              Else
                  state = #LexerState_NonKeyword
                  keyword + Chr(char)
              EndIf
            EndIf 
        EndIf
        If oldstate <> state Or currentpos = endpos
            If oldstate = #LexerState_NonKeyword
                lkeyword.s = LCase(keyword)
                If FindString(#DS_EDITOR_OPENFOLD," "+LCase(keyword)+" ",1)
                    thislevel | #SC_FOLDLEVELHEADERFLAG
                    nextlevel + 1
                ElseIf FindString(#DS_EDITOR_CLOSEFOLD," "+LCase(keyword)+" ",1) 
                    nextlevel - 1
                    If nextlevel < #SC_FOLDLEVELBASE
                        nextlevel = #SC_FOLDLEVELBASE
                    EndIf
                EndIf
                
                If FindString(#DS_EDITOR_KEYWORDS," "+LCase(keyword)+" ",1)
                  oldstate = #LexerState_Keyword                                    
                EndIf                
                
                keyword = ""
            EndIf
            Scintilla(sciptr, #SCI_SETSTYLING, currentpos - startkeyword, oldstate)
            startkeyword = currentpos
        EndIf
       
        If char = 10 Or currentpos = endpos
            Scintilla(sciptr, #SCI_SETFOLDLEVEL, linenumber, thislevel)
            thislevel = nextlevel
            linenumber + 1
        EndIf
       
        currentpos + 1
    Wend
EndProcedure 

ProcedureDLL ScCallBack(EditorGadget.l, *scinotify.SCNotification)
  If *scinotify\nmhdr\code = #SCN_STYLENEEDED
    Highlight(1, -1, *scinotify\position) 
  ElseIf *scinotify\nmhdr\code = #SCN_MARGINCLICK
    modifiers = *scinotify\modifiers
    position = *scinotify\position
    margin = *scinotify\margin
    linenumber = Scintilla(1, #SCI_LINEFROMPOSITION, position)
    Select margin
        Case 2
            Scintilla(1, #SCI_TOGGLEFOLD, linenumber)
    EndSelect 
  EndIf
EndProcedure

OpenWindow(0,0,0,600,600,"")

RemoveKeyboardShortcut(0, #PB_Shortcut_Tab)
RemoveKeyboardShortcut(0, #PB_Shortcut_Tab | #PB_Shortcut_Shift)

InitScintilla("Scintilla.dll")
CreateGadgetList(WindowID(0))
ScintillaGadget(1,0,0,600,500,@ScCallBack())
ButtonGadget(2,0,510,100,50,"GetText")

; Margins
Scintilla(1, #SCI_STYLESETSIZE, #STYLE_DEFAULT, 10) 
Scintilla(1, #SCI_SETMARGINTYPEN, 0, #SC_MARGIN_NUMBER)
Scintilla(1, #SCI_SETMARGINMASKN, 2, #SC_MASK_FOLDERS)
Scintilla(1, #SCI_SETMARGINWIDTHN, 0, 34)
Scintilla(1, #SCI_SETMARGINWIDTHN, 1, 1)
Scintilla(1, #SCI_SETMARGINWIDTHN, 2, 15)
Scintilla(1, #SCI_SETMARGINSENSITIVEN, 2, #True)

; Choose folding icons
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDEROPEN, #SC_MARK_CIRCLEMINUS)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDER, #SC_MARK_CIRCLEPLUS)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDERSUB, #SC_MARK_VLINE)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDERTAIL, #SC_MARK_LCORNERCURVE)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDEREND, #SC_MARK_CIRCLEPLUSCONNECTED)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDEROPENMID, #SC_MARK_CIRCLEMINUSCONNECTED)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDERMIDTAIL, #SC_MARK_TCORNERCURVE)

; Choose folding icon colours
Scintilla(1, #SCI_MARKERSETFORE, #SC_MARKNUM_FOLDER, $FFFFFF)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDER, 0)
Scintilla(1, #SCI_MARKERSETFORE, #SC_MARKNUM_FOLDEROPEN, $FFFFFF)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDEROPEN, 0)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDEROPENMID, 0)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDERSUB, 0)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDERTAIL, 0)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDERMIDTAIL, 0) 

;lex setup
ScintillaSendMessage(1,#SCI_SETLEXER, #SCLEX_CONTAINER, 0);
ScintillaSendMessage(1,#SCI_STYLESETFORE, #STYLE_DEFAULT, RGB(0,0,0));
ScintillaSendMessage(1,#SCI_STYLESETBACK, #STYLE_DEFAULT, RGB(255,255,255));
ScintillaSendMessage(1,#SCI_STYLECLEARALL);

; Set caret line colour
ScintillaSendMessage(1, #SCI_SETCARETLINEBACK, RGB(231, 245, 255))
ScintillaSendMessage(1, #SCI_SETCARETLINEVISIBLE, #True)

; Set styles for custom lexer
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_Comment, $8800)
ScintillaSendMessage(1, #SCI_STYLESETITALIC, #LexerState_Comment, 1)
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_NonKeyword, 0)
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_Keyword, 0)
ScintillaSendMessage(1, #SCI_STYLESETBOLD, #LexerState_NonKeyword, 0)
ScintillaSendMessage(1, #SCI_STYLESETBOLD, #LexerState_Keyword, 1)
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_FoldKeyword, $ff) 
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_Constant, RGB(180,0,0))
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_String, RGB(0,100,170))

SetGadgetText(1,";test comment"+#CRLF$+"If abc=1"+#CRLF$+"   Goto hi_there"+#CRLF$+"EndIf")

Repeat

 event=WaitWindowEvent()

 If event=#PB_Event_Gadget
   If EventGadget()=2
     MessageRequester("Get",GetGadgetText(1))
   EndIf
 EndIf

Until event=#PB_Event_CloseWindow
End

dracflamloc
Addict
Addict
Posts: 1648
Joined: Mon Sep 20, 2004 3:52 pm
Contact:

Post by dracflamloc »

dracflamloc wrote:Here is a modified program tailored to DracScript, for anyone whos interested.

Code: Select all

#DS_EDITOR_KEYWORDS = " if else elseif endif exit free while wend goto label "
#DS_EDITOR_OPENFOLD = " while if "
#DS_EDITOR_CLOSEFOLD = " wend endif "
#DS_EDITOR_ENDCONSTANT = "+=-*/%$ !<>|\/.,()"

Enumeration 0
    #LexerState_Space
    #LexerState_Comment
    #LexerState_NonKeyword
    #LexerState_Keyword
    #LexerState_FoldKeyword
    #LexerState_Constant
    #LexerState_String
EndEnumeration

Procedure.l Scintilla(sciptr.l, p1.l, p2.l=0, p3.l=0)
  ProcedureReturn ScintillaSendMessage(sciptr,p1,p2,p3)
EndProcedure

Procedure Highlight(sciptr.l, startpos.l, endpos.l)
    Debug(sciptr)
    If startpos = -1
        endstyled.l = Scintilla(sciptr, #SCI_GETENDSTYLED)
        linenumber.l = Scintilla(sciptr, #SCI_LINEFROMPOSITION, endstyled)
    Else
        linenumber = Scintilla(sciptr, #SCI_LINEFROMPOSITION, startpos)
    EndIf
   
    If linenumber = 0
        level = #SC_FOLDLEVELBASE
    Else
        linenumber - 1
        level = Scintilla(sciptr, #SCI_GETFOLDLEVEL, linenumber) & ~ #SC_FOLDLEVELHEADERFLAG
    EndIf
   
    thislevel.l = level
    nextlevel.l = level
   
    currentpos.l = Scintilla(sciptr, #SCI_POSITIONFROMLINE, linenumber)
    Scintilla(sciptr, #SCI_STARTSTYLING, currentpos, $1f | #INDICS_MASK)
    state = #LexerState_Space
    startkeyword = currentpos
    keyword.s = ""
   
    qpos.l = -1 ;quote pos
    While currentpos <= endpos
        oldstate = state
        lastchar.l = char.l        
        char.l = Scintilla(sciptr, #SCI_GETCHARAT, currentpos)
        If char = ';'
            state = #LexerState_Comment
        ElseIf char = '"' And state<>#LexerState_String And state<>#LexerState_Comment
            state = #LexerState_String
            qpos = currentpos
        ElseIf state = #LexerState_String
            If (qpos<>currentpos-1 And lastchar = '"') Or char = 10 Or char = 13
              state=#LexerState_Space
              qpos=-1
            EndIf         
        ElseIf char = 10 Or char = 13
            state = #LexerState_Space
        ElseIf state <> #LexerState_Comment
            If state=#LexerState_Constant
              If FindString(#DS_EDITOR_ENDCONSTANT,Chr(char),1) Or char=10 Or char=13 Or char=9
                state=#LexerState_Space
              EndIf 
            Else             
              If char='#'
                  state = #LexerState_Constant                
              ElseIf char = 9 Or char = ' ' Or char = '.'
                  state = #LexerState_Space
              Else
                  state = #LexerState_NonKeyword
                  keyword + Chr(char)
              EndIf
            EndIf 
        EndIf
        If oldstate <> state Or currentpos = endpos
            If oldstate = #LexerState_NonKeyword
                lkeyword.s = LCase(keyword)
                If FindString(#DS_EDITOR_OPENFOLD," "+LCase(keyword)+" ",1)
                    thislevel | #SC_FOLDLEVELHEADERFLAG
                    nextlevel + 1
                ElseIf FindString(#DS_EDITOR_CLOSEFOLD," "+LCase(keyword)+" ",1) 
                    nextlevel - 1
                    If nextlevel < #SC_FOLDLEVELBASE
                        nextlevel = #SC_FOLDLEVELBASE
                    EndIf
                EndIf
                
                If FindString(#DS_EDITOR_KEYWORDS," "+LCase(keyword)+" ",1)
                  oldstate = #LexerState_Keyword                                    
                EndIf                
                
                keyword = ""
            EndIf
            Scintilla(sciptr, #SCI_SETSTYLING, currentpos - startkeyword, oldstate)
            startkeyword = currentpos
        EndIf
       
        If char = 10 Or currentpos = endpos
            Scintilla(sciptr, #SCI_SETFOLDLEVEL, linenumber, thislevel)
            thislevel = nextlevel
            linenumber + 1
        EndIf
       
        currentpos + 1
    Wend
EndProcedure 

ProcedureDLL ScCallBack(EditorGadget.l, *scinotify.SCNotification)
  If *scinotify\nmhdr\code = #SCN_STYLENEEDED
    Highlight(1, -1, *scinotify\position) 
  ElseIf *scinotify\nmhdr\code = #SCN_MARGINCLICK
    modifiers = *scinotify\modifiers
    position = *scinotify\position
    margin = *scinotify\margin
    linenumber = Scintilla(1, #SCI_LINEFROMPOSITION, position)
    Select margin
        Case 2
            Scintilla(1, #SCI_TOGGLEFOLD, linenumber)
    EndSelect 
  EndIf
EndProcedure

OpenWindow(0,0,0,600,600,"")

RemoveKeyboardShortcut(0, #PB_Shortcut_Tab)
RemoveKeyboardShortcut(0, #PB_Shortcut_Tab | #PB_Shortcut_Shift)

InitScintilla("Scintilla.dll")
CreateGadgetList(WindowID(0))
ScintillaGadget(1,0,0,600,500,@ScCallBack())
ButtonGadget(2,0,510,100,50,"GetText")

; Margins
Scintilla(1, #SCI_STYLESETSIZE, #STYLE_DEFAULT, 10) 
Scintilla(1, #SCI_SETMARGINTYPEN, 0, #SC_MARGIN_NUMBER)
Scintilla(1, #SCI_SETMARGINMASKN, 2, #SC_MASK_FOLDERS)
Scintilla(1, #SCI_SETMARGINWIDTHN, 0, 34)
Scintilla(1, #SCI_SETMARGINWIDTHN, 1, 1)
Scintilla(1, #SCI_SETMARGINWIDTHN, 2, 15)
Scintilla(1, #SCI_SETMARGINSENSITIVEN, 2, #True)

; Choose folding icons
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDEROPEN, #SC_MARK_CIRCLEMINUS)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDER, #SC_MARK_CIRCLEPLUS)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDERSUB, #SC_MARK_VLINE)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDERTAIL, #SC_MARK_LCORNERCURVE)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDEREND, #SC_MARK_CIRCLEPLUSCONNECTED)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDEROPENMID, #SC_MARK_CIRCLEMINUSCONNECTED)
Scintilla(1, #SCI_MARKERDEFINE, #SC_MARKNUM_FOLDERMIDTAIL, #SC_MARK_TCORNERCURVE)

; Choose folding icon colours
Scintilla(1, #SCI_MARKERSETFORE, #SC_MARKNUM_FOLDER, $FFFFFF)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDER, 0)
Scintilla(1, #SCI_MARKERSETFORE, #SC_MARKNUM_FOLDEROPEN, $FFFFFF)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDEROPEN, 0)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDEROPENMID, 0)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDERSUB, 0)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDERTAIL, 0)
Scintilla(1, #SCI_MARKERSETBACK, #SC_MARKNUM_FOLDERMIDTAIL, 0) 

;lex setup
ScintillaSendMessage(1,#SCI_SETLEXER, #SCLEX_CONTAINER, 0);
ScintillaSendMessage(1,#SCI_STYLESETFORE, #STYLE_DEFAULT, RGB(0,0,0));
ScintillaSendMessage(1,#SCI_STYLESETBACK, #STYLE_DEFAULT, RGB(255,255,255));
ScintillaSendMessage(1,#SCI_STYLECLEARALL);

; Set caret line colour
ScintillaSendMessage(1, #SCI_SETCARETLINEBACK, RGB(231, 245, 255))
ScintillaSendMessage(1, #SCI_SETCARETLINEVISIBLE, #True)

; Set styles for custom lexer
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_Comment, $8800)
ScintillaSendMessage(1, #SCI_STYLESETITALIC, #LexerState_Comment, 1)
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_NonKeyword, 0)
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_Keyword, 0)
ScintillaSendMessage(1, #SCI_STYLESETBOLD, #LexerState_NonKeyword, 0)
ScintillaSendMessage(1, #SCI_STYLESETBOLD, #LexerState_Keyword, 1)
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_FoldKeyword, $ff) 
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_Constant, RGB(180,0,0))
ScintillaSendMessage(1, #SCI_STYLESETFORE, #LexerState_String, RGB(0,100,170))

SetGadgetText(1,";test comment"+#CRLF$+"If abc=1"+#CRLF$+"   Goto hi_there"+#CRLF$+"EndIf")

Repeat

 event=WaitWindowEvent()

 If event=#PB_Event_Gadget
   If EventGadget()=2
     MessageRequester("Get",GetGadgetText(1))
   EndIf
 EndIf

Until event=#PB_Event_CloseWindow
End

User avatar
waffle
Enthusiast
Enthusiast
Posts: 129
Joined: Mon May 12, 2003 1:34 pm
Location: USA
Contact:

Re: Custom Scintilla lexer

Post by waffle »

Found a bug in the search function ...

Code: Select all


Procedure FindText()
    text.s = InputRequester("Scintilla test", "Enter text to search for", "")
    If text <> ""
        current = Scintilla(sciptr, #SCI_GETCURRENTPOS)
        Scintilla(sciptr, #SCI_SETANCHOR, current)
        Scintilla(sciptr, #SCI_SEARCHANCHOR)
        *UTF8=UTF8(text); NEED TO ADD THIS LINE
        start = Scintilla(sciptr, #SCI_SEARCHNEXT, 0, *UTF8); CHANGE @text to *UTF8
        FreeMemory(*UTF8); AND FREE MEMORY
        If start <> -1
            anchor = Scintilla(sciptr, #SCI_GETANCHOR)
            Scintilla(sciptr, #SCI_SETSEL, start, anchor)
        EndIf
    EndIf
    SetFocus_(scihWnd)
EndProcedure
love the code, I used the original for me DBC editor 2002 ??? I must be getting old :)
Code is good... Its an international language.
Mesa
Enthusiast
Enthusiast
Posts: 433
Joined: Fri Feb 24, 2012 10:19 am

Re: Custom Scintilla lexer

Post by Mesa »

It crashes when i use the menu open (file).

M.
User avatar
waffle
Enthusiast
Enthusiast
Posts: 129
Joined: Mon May 12, 2003 1:34 pm
Location: USA
Contact:

Re: Custom Scintilla lexer

Post by waffle »

I tried differnt things ... the best I came up with for now was

Code: Select all

If char<0
    Debug("forcing char to 0")
    char=0
 EndIf
there is something happening in the loading code ....
I tried

Code: Select all

Procedure LoadFile()
    filename.s = OpenFileRequester("Open a file...", "", "PureBasic (*.pb)|*.pb|All files (*.*)|*.*", 0)
    If filename <> ""
        file = ReadFile(#PB_Any, filename)
        If file <> 0
            ;len = Lof(file)
            ;*mem = AllocateMemory(len)
            text.s=ReadString(file, #PB_File_IgnoreEOL)
            *mem=UTF8(text)
            ;If *mem
                ;ReadData(file, *mem, len)
              
                Scintilla(sciptr, #SCI_SETTEXT, 0, *mem)
                FreeMemory(*mem)
            ;EndIf
            CloseFile(file)
        EndIf
    EndIf
EndProcedure

and it still stopped at the same point ...
I will tinker more in the AM :?
Code is good... Its an international language.
User avatar
waffle
Enthusiast
Enthusiast
Posts: 129
Joined: Mon May 12, 2003 1:34 pm
Location: USA
Contact:

Re: Custom Scintilla lexer

Post by waffle »

this seems the best i can do ...

Code: Select all


Procedure LoadFile()
    filename.s = OpenFileRequester("Open a file...", "", "PureBasic (*.pb)|*.pb|All files (*.*)|*.*", 0)
    If filename <> ""
        file = ReadFile(#PB_Any, filename)
        If file <> 0
            
            text.s=ReadString(file, #PB_File_IgnoreEOL )
            *mem=UTF8(text)
            If *mem
                
                Scintilla(sciptr, #SCI_SETTEXT, 0, *mem)
                FreeMemory(*mem)
            EndIf
            CloseFile(file)
        EndIf
    EndIf
EndProcedure
and then in the highlight code

Code: Select all


    While currentpos <= endpos
        oldstate = state
        char = Scintilla(sciptr, #SCI_GETCHARAT, currentpos)
        ;- bugfix
        If char<0
          Debug("forcing char to 0 at "+Str(currentpos))
          char=0
        EndIf
I noticed it only needs to force char to 0 for the first 3 bytes ...
so, this should not affect any coloring code
Code is good... Its an international language.
Post Reply