Understandable. I wonder if compiling the entire IDE as a DLL file and exposing a method to inject the main form (with all UI hidden except the editor panel and tabs) would be viable?
In case anyone else comes across this post, here is what I'm using for RichText control based highlighting. I had done something similar in VB6 many years ago, but can't quite recall how I solved the scrolling issue. I'll keep poking at it.
Code: Select all
PROTECTED oRtbScriptCode AS RichTextBox
PROTECTED oRtbHighlighBackBuffer AS RichTextBox
// Syntax highlighting timer
PROTECTED oHighlightTimer AS Timer
PROTECCTED METHOD InitializeScriptEditorPanel() AS VOID
SELF:oRtbScriptCode := RichTextBox{}
SELF:oRtbScriptCode:Location := System.Drawing.Point{10, nYPos}
SELF:oRtbScriptCode:Anchor := AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right
SELF:oRtbScriptCode:Size := Size{275, 150}
SELF:oRtbScriptCode:Font := System.Drawing.Font{"Consolas", 10, FontStyle.Regular}
SELF:oRtbScriptCode:ScrollBars := RichTextBoxScrollBars.ForcedVertical
SELF:oRtbScriptCode:WordWrap := TRUE
SELF:oRtbScriptCode:AcceptsTab := TRUE
SELF:oRtbScriptCode:TextChanged += RtbScriptCode_TextChanged
SELF:oRtbHighlighBackBuffer := RichTextBox{}
SELF:oRtbHighlighBackBuffer:Visible := FALSE
SELF:oRtbScriptCode:Font := System.Drawing.Font{"Consolas", 10, FontStyle.Regular}
// Initialize syntax highlighting timer
SELF:oHighlightTimer := Timer{}
SELF:oHighlightTimer:Interval := 500 // 0.5 seconds
SELF:oHighlightTimer:Tick += HighlightTimer_Tick
END METHOD
PRIVATE METHOD RtbScriptCode_TextChanged(sender AS OBJECT, e AS EventArgs) AS VOID
// Ignore text changes triggered by undo/redo operations
IF SELF:lIgnoreTextChanges
RETURN
ENDIF
SELF:lScriptModified := TRUE
SELF:UpdateFormTitle()
// Reset syntax highlighting timer
SELF:oHighlightTimer:Stop()
SELF:oHighlightTimer:Start()
END METHOD
PRIVATE METHOD HighlightTimer_Tick(sender AS OBJECT, e AS EventArgs) AS VOID
SELF:oHighlightTimer:Stop()
// Push current state to undo stack before applying syntax highlighting
SELF:oScriptCodeUndoRedoManager:PushState(SELF:oRtbScriptCode:Text)
SELF:oRtbScriptCode:ClearUndo()
// Apply syntax highlighting
SELF:ApplySyntaxHighlighting()
END METHOD
PRIVATE METHOD ApplySyntaxHighlighting() AS VOID
STATIC scLastText := "" AS STRING
IF SELF:oRtbScriptCode:Text:Length == 0 .OR. SELF:oRtbScriptCode:Text == scLastText
RETURN
ENDIF
scLastText := SELF:oRtbScriptCode:Text
SELF:oRtbHighlighBackBuffer:Text := SELF:oRtbScriptCode:Text
// Save current selection and scroll position
LOCAL nSelStart := SELF:oRtbScriptCode:SelectionStart AS INT
LOCAL nSelLength := SELF:oRtbScriptCode:SelectionLength AS INT
TRY
// Disable redrawing during highlighting
SELF:oRtbHighlighBackBuffer:SuspendLayout()
// Reset all formatting
SELF:oRtbHighlighBackBuffer:SelectAll()
SELF:oRtbHighlighBackBuffer:SelectionColor := System.Drawing.Color.Black
SELF:oRtbHighlighBackBuffer:SelectionFont := System.Drawing.Font{"Consolas", 10, FontStyle.Regular}
// Apply language-specific highlighting
LOCAL cLanguage := SELF:oCboLanguage:SelectedItem:ToString():ToUpper() AS STRING
SWITCH cLanguage
CASE "T-SQL"
SELF:ApplySqlSyntaxHighlighting()
CASE "POSTGRESQL"
SELF:ApplyPostgreSqlSyntaxHighlighting()
CASE "XSHARP"
SELF:ApplyXSharpSyntaxHighlighting()
END SWITCH
FINALLY
SELF:oRtbScriptCode:Rtf := SELF:oRtbHighlighBackBuffer:Rtf
// Restore selection and scroll position
SELF:oRtbScriptCode:Select(nSelStart, nSelLength)
SELF:oRtbScriptCode:ResumeLayout()
END TRY
END METHOD
PRIVATE METHOD ApplySqlSyntaxHighlighting() AS VOID
LOCAL cText := SELF:oRtbScriptCode:Text AS STRING
// SQL Keywords
LOCAL aSqlKeywords := <STRING>{"SELECT", "FROM", "WHERE", "INSERT", "UPDATE", "DELETE", "CREATE", "ALTER", "DROP", ;
"TABLE", "INDEX", "VIEW", "PROCEDURE", "FUNCTION", "DECLARE", "SET", "IF", "ELSE", ;
"BEGIN", "END", "WHILE", "FOR", "CASE", "WHEN", "THEN", "AS", "AND", "OR", "NOT", ;
"IN", "EXISTS", "BETWEEN", "LIKE", "IS", "NULL", "ORDER", "BY", "GROUP", "HAVING", ;
"UNION", "JOIN", "INNER", "OUTER", "LEFT", "RIGHT", "ON", "DISTINCT", "TOP"} AS STRING[]
// Highlight keywords
FOREACH cKeyword AS STRING IN aSqlKeywords
SELF:HighlightPattern(i"\b{cKeyword}\b", System.Drawing.Color.Blue, FontStyle.Bold, TRUE)
NEXT
// Highlight strings
SELF:HighlightPattern("'[^']*'", System.Drawing.Color.Red, FontStyle.Regular, FALSE)
// Highlight comments
SELF:HighlightPattern("--.*$", System.Drawing.Color.Green, FontStyle.Italic, FALSE)
SELF:HighlightPattern("/\*.*?\*/", System.Drawing.Color.Green, FontStyle.Italic, FALSE)
// Highlight numbers
SELF:HighlightPattern("\b\d+(\.\d+)?\b", System.Drawing.Color.Purple, FontStyle.Regular, FALSE)
END METHOD
PRIVATE METHOD ApplyPostgreSqlSyntaxHighlighting() AS VOID
LOCAL cText := SELF:oRtbScriptCode:Text AS STRING
// PostgreSQL Keywords (similar to SQL with some additions)
LOCAL aPgKeywords := <STRING>{"SELECT", "FROM", "WHERE", "INSERT", "UPDATE", "DELETE", "CREATE", "ALTER", "DROP", ;
"TABLE", "INDEX", "VIEW", "FUNCTION", "DECLARE", "SET", "IF", "ELSE", ;
"BEGIN", "END", "WHILE", "FOR", "CASE", "WHEN", "THEN", "AS", "AND", "OR", "NOT", ;
"IN", "EXISTS", "BETWEEN", "LIKE", "ILIKE", "IS", "NULL", "ORDER", "BY", "GROUP", "HAVING", ;
"UNION", "JOIN", "INNER", "OUTER", "LEFT", "RIGHT", "ON", "DISTINCT", "LIMIT", "OFFSET"} AS STRING[]
// Highlight keywords
FOREACH cKeyword AS STRING IN aPgKeywords
SELF:HighlightPattern(i"\b{cKeyword}\b", System.Drawing.Color.Blue, FontStyle.Bold, TRUE)
NEXT
// Highlight strings
SELF:HighlightPattern("'[^']*'", System.Drawing.Color.Red, FontStyle.Regular, FALSE)
// Highlight comments
SELF:HighlightPattern("--.*$", System.Drawing.Color.Green, FontStyle.Italic, FALSE)
SELF:HighlightPattern("/\*.*?\*/", System.Drawing.Color.Green, FontStyle.Italic, FALSE)
// Highlight numbers
SELF:HighlightPattern("\b\d+(\.\d+)?\b", System.Drawing.Color.Purple, FontStyle.Regular, FALSE)
END METHOD
PRIVATE METHOD ApplyXSharpSyntaxHighlighting() AS VOID
LOCAL cText := SELF:oRtbScriptCode:Text AS STRING
// XSharp Keywords
LOCAL aXSharpKeywords := <STRING>{"USING", "CLASS", "METHOD", "FUNCTION", "PROCEDURE", "INTERFACE", "PROPERTY", ;
"PUBLIC", "PRIVATE", "PROTECTED", "STATIC", "VIRTUAL", "OVERRIDE", "ABSTRACT", ;
"IF", "ELSE", "ELSEIF", "ENDIF", "FOR", "FOREACH", "NEXT", "WHILE", "ENDDO", ;
"DO", "CASE", "OTHERWISE", "ENDCASE", "TRY", "CATCH", "FINALLY", "END", ;
"RETURN", "LOCAL", "AS", "IS", "AND", "OR", "NOT", "TRUE", "FALSE", "NULL"} AS STRING[]
// Highlight keywords
FOREACH cKeyword AS STRING IN aXSharpKeywords
SELF:HighlightPattern(i"\b{cKeyword}\b", System.Drawing.Color.Blue, FontStyle.Bold, TRUE)
NEXT
// Highlight strings
SELF:HighlightPattern('"[^"]*"', System.Drawing.Color.Red, FontStyle.Regular, FALSE)
SELF:HighlightPattern("'[^']*'", System.Drawing.Color.Red, FontStyle.Regular, FALSE)
// Highlight comments
SELF:HighlightPattern("//.*$", System.Drawing.Color.Green, FontStyle.Italic, FALSE)
SELF:HighlightPattern("/\*.*?\*/", System.Drawing.Color.Green, FontStyle.Italic, FALSE)
SELF:HighlightPattern("\*.*$", System.Drawing.Color.Green, FontStyle.Italic, FALSE)
// Highlight numbers
SELF:HighlightPattern("\b\d+(\.\d+)?\b", System.Drawing.Color.Purple, FontStyle.Regular, FALSE)
END METHOD
PRIVATE METHOD HighlightPattern(cPattern AS STRING, oColor AS System.Drawing.Color, oStyle AS FontStyle, lIgnoreCase AS LOGIC) AS VOID
TRY
LOCAL oRegex AS Regex
LOCAL oOptions := RegexOptions.Multiline AS RegexOptions
IF lIgnoreCase
oOptions |= RegexOptions.IgnoreCase
ENDIF
oRegex := Regex{cPattern, oOptions}
LOCAL oMatches := oRegex:Matches(SELF:oRtbHighlighBackBuffer:Text) AS MatchCollection
FOREACH oMatch AS Match IN oMatches
SELF:oRtbHighlighBackBuffer:Select(oMatch:Index, oMatch:Length)
SELF:oRtbHighlighBackBuffer:SelectionColor := oColor
SELF:oRtbHighlighBackBuffer:SelectionFont := System.Drawing.Font{"Consolas", 10, oStyle}
NEXT
CATCH oEx AS Exception
// Ignore regex errors for now
END TRY
END METHOD