[Editor] Add support for SQL script type | enable SQL syntax highlighting

This commit is contained in:
2021-03-25 11:07:23 +01:00
parent cbb03cdc84
commit 80b4621f39
6 changed files with 261 additions and 2 deletions

View File

@@ -0,0 +1,167 @@
package com.bartlomiejpluta.base.editor.code.highlighting
import com.bartlomiejpluta.base.editor.code.stylesheet.SqlSyntaxHighlightingStylesheet
import org.fxmisc.richtext.model.StyleSpans
import org.fxmisc.richtext.model.StyleSpansBuilder
import org.springframework.stereotype.Component
@Component
class SqlSyntaxHighlighter : SyntaxHighlighter {
override fun highlight(code: String): StyleSpans<Collection<String>> = StyleSpansBuilder<Collection<String>>().let {
val lastKeywordEnd = PATTERN.findAll(code).fold(0) { lastKeywordEnd, result ->
val styleClass = when {
result.groups["KEYWORD"] != null -> "keyword"
result.groups["PAREN"] != null -> "paren"
result.groups["BRACE"] != null -> "brace"
result.groups["BRACKET"] != null -> "bracket"
result.groups["SEMICOLON"] != null -> "semicolon"
result.groups["STRING"] != null -> "string"
result.groups["REF"] != null -> "ref"
result.groups["QUOTED"] != null -> "quoted"
result.groups["NUMBER"] != null -> "number"
result.groups["OPERATOR"] != null -> "operator"
result.groups["COMMENT"] != null -> "comment"
else -> throw IllegalStateException("Unsupported regex group")
}
it.add(emptyList(), result.range.first - lastKeywordEnd)
it.add(listOf(styleClass), result.range.last - result.range.first + 1)
result.range.last + 1
}
it.add(emptyList(), code.length - lastKeywordEnd)
it.create()
}
override val stylesheet = SqlSyntaxHighlightingStylesheet()
companion object {
private val KEYWORDS = arrayOf(
"A", "ABORT",
"ABS", "ABSOLUTE", "ACCESS", "ACTION", "ADA", "ADD", "ADMIN", "AFTER", "AGGREGATE", "ALIAS",
"ALL", "ALLOCATE", "ALSO", "ALTER", "ALWAYS", "ANALYSE", "ANALYZE", "AND", "ANY", "ARE", "ARRAY", "AS",
"ASC", "ASENSITIVE", "ASSERTION", "ASSIGNMENT", "ASYMMETRIC", "AT", "ATOMIC", "ATTRIBUTE", "ATTRIBUTES",
"AUDIT", "AUTHORIZATION", "AUTO_INCREMENT", "AVG", "AVG_ROW_LENGTH", "BACKUP", "BACKWARD", "BEFORE",
"BEGIN", "BERNOULLI", "BETWEEN", "BIGINT", "BINARY", "BIT", "BIT_LENGTH", "BITVAR", "BLOB", "BOOL",
"BOOLEAN", "BOTH", "BREADTH", "BREAK", "BROWSE", "BULK", "BY", "C", "CACHE", "CALL", "CALLED",
"CARDINALITY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CATALOG_NAME", "CEIL", "CEILING", "CHAIN",
"CHANGE", "CHAR", "CHAR_LENGTH", "CHARACTER", "CHARACTER_LENGTH", "CHARACTER_SET_CATALOG",
"CHARACTER_SET_NAME", "CHARACTER_SET_SCHEMA", "CHARACTERISTICS", "CHARACTERS", "CHECK", "CHECKED",
"CHECKPOINT", "CHECKSUM", "CLASS", "CLASS_ORIGIN", "CLOB", "CLOSE", "CLUSTER", "CLUSTERED", "COALESCE",
"COBOL", "COLLATE", "COLLATION", "COLLATION_CATALOG", "COLLATION_NAME", "COLLATION_SCHEMA", "COLLECT",
"COLUMN", "COLUMN_NAME", "COLUMNS", "COMMAND_FUNCTION", "COMMAND_FUNCTION_CODE", "COMMENT", "COMMIT",
"COMMITTED", "COMPLETION", "COMPRESS", "COMPUTE", "CONDITION", "CONDITION_NUMBER", "CONNECT", "CONNECTION",
"CONNECTION_NAME", "CONSTRAINT", "CONSTRAINT_CATALOG", "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CONSTRAINTS",
"CONSTRUCTOR", "CONTAINS", "CONTAINSTABLE", "CONTINUE", "CONVERSION", "CONVERT", "COPY", "CORR",
"CORRESPONDING", "COUNT", "COVAR_POP", "COVAR_SAMP", "CREATE", "CREATEDB", "CREATEROLE", "CREATEUSER",
"CROSS", "CSV", "CUBE", "CUME_DIST", "CURRENT", "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP",
"CURRENT_PATH", "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TRANSFORM_GROUP_FOR_TYPE",
"CURRENT_USER", "CURSOR", "CURSOR_NAME", "CYCLE", "DATA", "DATABASE", "DATABASES", "DATE", "DATETIME",
"DATETIME_INTERVAL_CODE", "DATETIME_INTERVAL_PRECISION", "DAY", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE",
"DAY_SECOND", "DAYOFMONTH", "DAYOFWEEK", "DAYOFYEAR", "DBCC", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE",
"DEFAULT", "DEFAULTS", "DEFERRABLE", "DEFERRED", "DEFINED", "DEFINER", "DEGREE", "DELAY_KEY_WRITE", "DELAYED",
"DELETE", "DELIMITER", "DELIMITES", "DENSE_RANK", "DENY", "DEPTH", "DEREF", "DERIVED", "DESC", "DESCRIBE",
"DESCRIPTOR", "DESTROY", "DESTRUCTOR", "DETERMINISTIC", "DIAGNOSTICS", "DICTIONARY", "DISABLE", "DISCONNECT",
"DISK", "DISPATCH", "DISTINCT", "DISTINCTROW", "DISTRIBUTED", "DIV", "DO", "DOMAIN", "DOUBLE", "DROP", "DUAL",
"DUMMY", "DUMP", "DYNAMIC", "DYNAMIC_FUNCTION", "DYNAMIC_FUNCTION_CODE", "EACH", "ELEMENT", "ELSE", "ELSEIF",
"ENABLE", "ENCLOSED", "ENCODING", "ENCRYPTED", "END", "END-EXEC", "ENUM", "EQUALS", "ERRLVL", "ESCAPE",
"ESCAPED", "EVERY", "EXCEPT", "EXCEPTION", "EXCLUDE", "EXCLUDING", "EXCLUSIVE", "EXEC", "EXECUTE", "EXISTING",
"EXISTS", "EXIT", "EXP", "EXPLAIN", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FIELDS", "FILE", "FILLFACTOR",
"FILTER", "FINAL", "FIRST", "FLOAT", "FLOAT4", "FLOAT8", "FLOOR", "FLUSH", "FOLLOWING", "FOR", "FORCE",
"FOREIGN", "FORTRAN", "FORWARD", "FOUND", "FREE", "FREETEXT", "FREETEXTTABLE", "FREEZE", "FROM", "FULL",
"FULLTEXT", "FUNCTION", "FUSION", "G", "GENERAL", "GENERATED", "GET", "GLOBAL", "GO", "GOTO", "GRANT",
"GRANTED", "GRANTS", "GREATEST", "GROUP", "GROUPING", "HANDLER", "HAVING", "HEADER", "HEAP", "HIERARCHY",
"HIGH_PRIORITY", "HOLD", "HOLDLOCK", "HOST", "HOSTS", "HOUR", "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND",
"IDENTIFIED", "IDENTITY", "IDENTITY_INSERT", "IDENTITYCOL", "IF", "IGNORE", "ILIKE", "IMMEDIATE", "IMMUTABLE",
"IMPLEMENTATION", "IMPLICIT", "IN", "INCLUDE", "INCLUDING", "INCREMENT", "INDEX", "INDICATOR", "INFILE",
"INFIX", "INHERIT", "INHERITS", "INITIAL", "INITIALIZE", "INITIALLY", "INNER", "INOUT", "INPUT", "INSENSITIVE",
"INSERT", "INSERT_ID", "INSTANCE", "INSTANTIABLE", "INSTEAD", "INT", "INT1", "INT2", "INT3", "INT4", "INT8",
"INTEGER", "INTERSECT", "INTERSECTION", "INTERVAL", "INTO", "INVOKER", "IS", "ISAM", "ISNULL", "ISOLATION",
"ITERATE", "JOIN", "K", "KEY", "KEY_MEMBER", "KEY_TYPE", "KEYS", "KILL", "LANCOMPILER", "LANGUAGE", "LARGE",
"LAST", "LAST_INSERT_ID", "LATERAL", "LEAD", "LEADING", "LEAST", "LEAVE", "LEFT", "LENGTH", "LESS", "LEVEL",
"LIKE", "LIMIT", "LINENO", "LINES", "LISTEN", "LN", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCATION",
"LOCATOR", "LOCK", "LOGIN", "LOGS", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "LOWER", "M",
"MAP", "MATCH", "MATCHED", "MAX", "MAX_ROWS", "MAXEXTENTS", "MAXVALUE", "MEDIUMBLOB", "MEDIUMINT",
"MEDIUMTEXT", "MEMBER", "MERGE", "MESSAGE_LENGTH", "MESSAGE_OCTET_LENGTH", "MESSAGE_TEXT", "METHOD",
"MIDDLEINT", "MIN", "MIN_ROWS", "MINUS", "MINUTE", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MINVALUE",
"MLSLABEL", "MOD", "MODE", "MODIFIES", "MODIFY", "MODULE", "MONTH", "MONTHNAME", "MORE", "MOVE", "MULTISET",
"MUMPS", "MYISAM", "NAME", "NAMES", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NESTING", "NEW", "NEXT", "NO",
"NO_WRITE_TO_BINLOG", "NOAUDIT", "NOCHECK", "NOCOMPRESS", "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER",
"NOINHERIT", "NOLOGIN", "NONCLUSTERED", "NONE", "NORMALIZE", "NORMALIZED", "NOSUPERUSER", "NOT", "NOTHING",
"NOTIFY", "NOTNULL", "NOWAIT", "NULL", "NULLABLE", "NULLIF", "NULLS", "NUMBER", "NUMERIC", "OBJECT",
"OCTET_LENGTH", "OCTETS", "OF", "OFF", "OFFLINE", "OFFSET", "OFFSETS", "OIDS", "OLD", "ON", "ONLINE", "ONLY",
"OPEN", "OPENDATASOURCE", "OPENQUERY", "OPENROWSET", "OPENXML", "OPERATION", "OPERATOR", "OPTIMIZE", "OPTION",
"OPTIONALLY", "OPTIONS", "OR", "ORDER", "ORDERING", "ORDINALITY", "OTHERS", "OUT", "OUTER", "OUTFILE",
"OUTPUT", "OVER", "OVERLAPS", "OVERLAY", "OVERRIDING", "OWNER", "PACK_KEYS", "PAD", "PARAMETER",
"PARAMETER_MODE", "PARAMETER_NAME", "PARAMETER_ORDINAL_POSITION", "PARAMETER_SPECIFIC_CATALOG",
"PARAMETER_SPECIFIC_NAME", "PARAMETER_SPECIFIC_SCHEMA", "PARAMETERS", "PARTIAL", "PARTITION", "PASCAL",
"PASSWORD", "PATH", "PCTFREE", "PERCENT", "PERCENT_RANK", "PERCENTILE_CONT", "PERCENTILE_DISC", "PLACING",
"PLAN", "PLI", "POSITION", "POSTFIX", "POWER", "PRECEDING", "PRECISION", "PREFIX", "PREORDER", "PREPARE",
"PREPARED", "PRESERVE", "PRIMARY", "PRINT", "PRIOR", "PRIVILEGES", "PROC", "PROCEDURAL", "PROCEDURE",
"PROCESS", "PROCESSLIST", "PUBLIC", "PURGE", "QUOTE", "RAID0", "RAISERROR", "RANGE", "RANK", "RAW", "READ",
"READS", "READTEXT", "REAL", "RECHECK", "RECONFIGURE", "RECURSIVE", "REF", "REFERENCES", "REFERENCING",
"REGEXP", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX",
"REGR_SXY", "REGR_SYY", "REINDEX", "RELATIVE", "RELEASE", "RELOAD", "RENAME", "REPEAT", "REPEATABLE",
"REPLACE", "REPLICATION", "REQUIRE", "RESET", "RESIGNAL", "RESOURCE", "RESTART", "RESTORE", "RESTRICT",
"RESULT", "RETURN", "RETURNED_CARDINALITY", "RETURNED_LENGTH", "RETURNED_OCTET_LENGTH", "RETURNED_SQLSTATE",
"RETURNS", "REVOKE", "RIGHT", "RLIKE", "ROLE", "ROLLBACK", "ROLLUP", "ROUTINE", "ROUTINE_CATALOG",
"ROUTINE_NAME", "ROUTINE_SCHEMA", "ROW", "ROW_COUNT", "ROW_NUMBER", "ROWCOUNT", "ROWGUIDCOL", "ROWID",
"ROWNUM", "ROWS", "RULE", "SAVE", "SAVEPOINT", "SCALE", "SCHEMA", "SCHEMA_NAME", "SCHEMAS", "SCOPE",
"SCOPE_CATALOG", "SCOPE_NAME", "SCOPE_SCHEMA", "SCROLL", "SEARCH", "SECOND", "SECOND_MICROSECOND", "SECTION",
"SECURITY", "SELECT", "SELF", "SENSITIVE", "SEPARATOR", "SEQUENCE", "SERIALIZABLE", "SERVER_NAME", "SESSION",
"SESSION_USER", "SET", "SETOF", "SETS", "SETUSER", "SHARE", "SHOW", "SHUTDOWN", "SIGNAL", "SIMILAR", "SIMPLE",
"SIZE", "SMALLINT", "SOME", "SONAME", "SOURCE", "SPACE", "SPATIAL", "SPECIFIC", "SPECIFIC_NAME",
"SPECIFICTYPE", "SQL", "SQL_BIG_RESULT", "SQL_BIG_SELECTS", "SQL_BIG_TABLES", "SQL_CALC_FOUND_ROWS",
"SQL_LOG_OFF", "SQL_LOG_UPDATE", "SQL_LOW_PRIORITY_UPDATES", "SQL_SELECT_LIMIT", "SQL_SMALL_RESULT",
"SQL_WARNINGS", "SQLCA", "SQLCODE", "SQLERROR", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "SQRT", "SSL",
"STABLE", "START", "STARTING", "STATE", "STATEMENT", "STATIC", "STATISTICS", "STATUS", "STDDEV_POP",
"STDDEV_SAMP", "STDIN", "STDOUT", "STORAGE", "STRAIGHT_JOIN", "STRICT", "STRING", "STRUCTURE", "STYLE",
"SUBCLASS_ORIGIN", "SUBLIST", "SUBMULTISET", "SUBSTRING", "SUCCESSFUL", "SUM", "SUPERUSER", "SYMMETRIC",
"SYNONYM", "SYSDATE", "SYSID", "SYSTEM", "SYSTEM_USER", "TABLE", "TABLE_NAME", "TABLES", "TABLESAMPLE",
"TABLESPACE", "TEMP", "TEMPLATE", "TEMPORARY", "TERMINATE", "TERMINATED", "TEXT", "TEXTSIZE", "THAN",
"THEN", "TIES", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TINYBLOB", "TINYINT", "TINYTEXT",
"TO", "TOAST", "TOP", "TOP_LEVEL_COUNT", "TRAILING", "TRAN", "TRANSACTION", "TRANSACTION_ACTIVE",
"TRANSACTIONS_COMMITTED", "TRANSACTIONS_ROLLED_BACK", "TRANSFORM", "TRANSFORMS", "TRANSLATE", "TRANSLATION",
"TREAT", "TRIGGER", "TRIGGER_CATALOG", "TRIGGER_NAME", "TRIGGER_SCHEMA", "TRIM", "TRUE", "TRUNCATE", "TRUSTED",
"TSEQUAL", "TYPE", "UESCAPE", "UID", "UNBOUNDED", "UNCOMMITTED", "UNDER", "UNDO", "UNENCRYPTED", "UNION",
"UNIQUE", "UNKNOWN", "UNLISTEN", "UNLOCK", "UNNAMED", "UNNEST", "UNSIGNED", "UNTIL", "UPDATE", "UPDATETEXT",
"UPPER", "USAGE", "USE", "USER", "USER_DEFINED_TYPE_CATALOG", "USER_DEFINED_TYPE_CODE",
"USER_DEFINED_TYPE_NAME", "USER_DEFINED_TYPE_SCHEMA", "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP",
"VACUUM", "VALID", "VALIDATE", "VALIDATOR", "VALUE", "VALUES", "VAR_POP", "VAR_SAMP", "VARBINARY", "VARCHAR",
"VARCHAR2", "VARCHARACTER", "VARIABLE", "VARIABLES", "VARYING", "VERBOSE", "VIEW", "VOLATILE", "WAITFOR",
"WHEN", "WHENEVER", "WHERE", "WHILE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WORK", "WRITE",
"WRITETEXT", "X509", "XOR", "YEAR", "YEAR_MONTH", "ZEROFILL", "ZONE"
)
private val KEYWORD_PATTERN = "\\b(" + KEYWORDS.joinToString("|") + ")\\b"
private val PAREN_PATTERN = "\\(|\\)"
private val BRACE_PATTERN = "\\{|\\}"
private val BRACKET_PATTERN = "\\[|\\]"
private val SEMICOLON_PATTERN = "\\;"
private val STRING_PATTERN = "'([^\"\\\\]|\\\\.)*?'"
private val REF_PATTERN = "`([^\"\\\\]|\\\\.)*?`"
private val QUOTED_PATTERN = "\"([^\"\\\\]|\\\\.)*?\""
private val NUMBER_PATTERN = "\\b-?([0-9]*\\.[0-9]+|[0-9]+)\\w?\\b"
private val OPERATOR_PATTERN = "[+-/*:|&?<>=!]"
private val COMMENT_PATTERN = """
--[^
]*
""".trimIndent()
private val PATTERN = (
"(?<KEYWORD>$KEYWORD_PATTERN)"
+ "|(?<PAREN>$PAREN_PATTERN)"
+ "|(?<BRACE>$BRACE_PATTERN)"
+ "|(?<BRACKET>$BRACKET_PATTERN)"
+ "|(?<SEMICOLON>$SEMICOLON_PATTERN)"
+ "|(?<STRING>$STRING_PATTERN)"
+ "|(?<REF>$REF_PATTERN)"
+ "|(?<QUOTED>$QUOTED_PATTERN)"
+ "|(?<NUMBER>$NUMBER_PATTERN)"
+ "|(?<COMMENT>$COMMENT_PATTERN)"
+ "|(?<OPERATOR>$OPERATOR_PATTERN)"
).toRegex(RegexOption.IGNORE_CASE)
}
}

View File

@@ -2,5 +2,6 @@ package com.bartlomiejpluta.base.editor.code.model
enum class CodeType {
JAVA,
XML
XML,
SQL
}

View File

@@ -0,0 +1,87 @@
package com.bartlomiejpluta.base.editor.code.stylesheet
import javafx.scene.text.FontPosture
import javafx.scene.text.FontWeight
import tornadofx.*
class SqlSyntaxHighlightingStylesheet : CodeEditorStylesheet() {
companion object {
val keyword by cssclass()
val semicolon by cssclass()
val paren by cssclass()
val bracket by cssclass()
val brace by cssclass()
val string by cssclass()
val quoted by cssclass()
val ref by cssclass()
val number by cssclass()
val operator by cssclass()
val field by cssclass()
val comment by cssclass()
val paragraphBox by cssclass()
val paragraphText by cssclass()
val hasCaret by csspseudoclass()
val tabSize by cssproperty<Int>("-fx-tab-size")
}
init {
keyword {
fill = c("#000080")
fontWeight = FontWeight.BOLD
}
semicolon {
fontWeight = FontWeight.BOLD
}
paren {
fill = c("cadetblue")
fontWeight = FontWeight.BOLD
}
bracket {
fill = c("darkgreen")
fontWeight = FontWeight.BOLD
}
brace {
fill = c("teal")
fontWeight = FontWeight.BOLD
}
string {
fill = c("#008000")
}
quoted {
fill = c("#BBB529")
}
ref {
fill = c("#4A86E8")
}
number {
fill = c("#0000FF")
}
operator {
fill = c("#CC7832")
}
comment {
fill = c("#808080")
fontStyle = FontPosture.ITALIC
}
paragraphBox and hasCaret {
backgroundColor = multi(c("#f2f9fc"))
}
paragraphText {
tabSize.value = 3
}
}
}

View File

@@ -2,6 +2,7 @@ package com.bartlomiejpluta.base.editor.code.view.editor
import com.bartlomiejpluta.base.editor.code.component.CodeEditor
import com.bartlomiejpluta.base.editor.code.highlighting.JavaSyntaxHighlighter
import com.bartlomiejpluta.base.editor.code.highlighting.SqlSyntaxHighlighter
import com.bartlomiejpluta.base.editor.code.highlighting.XmlSyntaxHighlighter
import com.bartlomiejpluta.base.editor.code.model.CodeScope
import com.bartlomiejpluta.base.editor.code.model.CodeType
@@ -21,6 +22,7 @@ class CodeEditorView : View() {
private val javaSyntaxHighlighter: JavaSyntaxHighlighter by di()
private val xmlSyntaxHighlighter: XmlSyntaxHighlighter by di()
private val sqlSyntaxHighlighter: SqlSyntaxHighlighter by di()
private val codeVM = find<CodeVM>()
@@ -28,6 +30,7 @@ class CodeEditorView : View() {
when (codeVM.type!!) {
CodeType.JAVA -> javaSyntaxHighlighter
CodeType.XML -> xmlSyntaxHighlighter
CodeType.SQL -> sqlSyntaxHighlighter
}
}, codeVM.typeProperty)

View File

@@ -39,7 +39,7 @@ class TablesListView : View() {
button("SQL Script", graphic = FontIcon("fa-code")) {
action {
mainController.openScript(
fsNode = InMemoryStringFileNode("script", "java", "SELECT * FROM myTable;"),
fsNode = InMemoryStringFileNode("Script", "sql", "SELECT * FROM myTable;"),
execute = { code -> databaseService.run { executeScript(code, this) } },
saveable = false
)

View File

@@ -253,6 +253,7 @@ class DefaultProjectContext : ProjectContext {
when (fileNode.extension.toLowerCase()) {
"java" -> CodeType.JAVA
"xml" -> CodeType.XML
"sql" -> CodeType.SQL
else -> throw IllegalStateException("Unsupported script type")
}
}))