diff --git a/src/application.vala b/src/application.vala index 370d7f6..6f7be87 100644 --- a/src/application.vala +++ b/src/application.vala @@ -222,6 +222,7 @@ namespace Psequel { var repository = new ConnectionRepository (Application.settings); var navigation = new NavigationService (); var export = new ExportService (); + var completer = new CompleterService (sql_service); // viewmodels var conn_vm = new ConnectionViewModel (repository, sql_service, navigation); @@ -236,6 +237,7 @@ namespace Psequel { var query_vm = new QueryViewModel (query_history_vm); container.register (sql_service); + container.register (completer); container.register (schema_service); container.register (export); container.register (repository); @@ -254,6 +256,7 @@ namespace Psequel { // events conn_vm.subcribe (Event.ACTIVE_CONNECTION, sche_vm); + sche_vm.subcribe (Event.SCHEMA_CHANGED, completer); sche_vm.subcribe (Event.SCHEMA_CHANGED, table_vm); sche_vm.subcribe (Event.SCHEMA_CHANGED, view_vm); sche_vm.subcribe (Event.SCHEMA_CHANGED, table_structure_vm); diff --git a/src/meson.build b/src/meson.build index d34594b..4c4da12 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,22 +9,19 @@ psequel_sources = [ 'ui/schema/SchemaView.vala', 'ui/schema/SchemaSidebar.vala', 'ui/schema/SchemaMain.vala', - - 'ui/schema/QueryEditor.vala', 'ui/schema/QueryResult.vala', - - 'ui/schema/TableStructureView.vala', 'ui/schema/ViewStructureView.vala', 'ui/schema/TableColumnInfo.vala', 'ui/schema/TableForeignKeyInfo.vala', 'ui/schema/TableIndexInfo.vala', - 'ui/schema/TableDataView.vala', 'ui/schema/ViewDataView.vala', + + 'ui/editor/QueryEditor.vala', + 'ui/editor/SQLCompletionProvider.vala', 'ui/widgets/StyleSwitcher.vala', - 'ui/Window.vala', 'ui/PreferencesWindow.vala', @@ -36,13 +33,13 @@ psequel_sources = [ # 'models/utils.vala', 'services/SQLService.vala', + 'services/CompleterService.vala', 'services/ExportService.vala', 'services/NavigationService.vala', 'services/ConnectionService.vala', 'services/Container.vala', 'services/ResourceManager.vala', 'services/SchemaService.vala', - 'services/SQLCompletionProvider.vala', 'repositories/ConnectionRepository.vala', 'repositories/QueryRepository.vala', diff --git a/src/services/CompleterService.vala b/src/services/CompleterService.vala new file mode 100644 index 0000000..d7bcb7a --- /dev/null +++ b/src/services/CompleterService.vala @@ -0,0 +1,163 @@ +namespace Psequel { + public class CompleterService : Object, Observer { + public SQLService sql_service { get; construct; } + + public List schemas {get; owned set;} + public List tables {get; owned set;} + public List columns {get; owned set;} + + // public List keywords { get; owned set; } + + public CompleterService (SQLService sql_service) { + Object (sql_service: sql_service); + } + + public List get_suggestions (SchemaContext context, string last_word) { + // context maybe use in the future for smart completion or something. + debug ("Lastword: %s", last_word); + + var keywords = suggest_keywords (last_word); + var tables = suggest_tables (last_word); + var columns = suggest_columns (last_word); + var schemas = suggest_schemas (last_word); + + tables.concat ((owned)columns); + schemas.concat ((owned)tables); + keywords.concat ((owned)schemas); + + return keywords; + } + + public void update (Event event) { + if (event.type == Event.SCHEMA_CHANGED) { + var schema = (Schema) event.data; + refresh_data (schema); + } + } + + private List suggest_keywords (string prefix = "") { + var candidates = new List (); + + for (int i = 0; i < PGListerals.KEYWORDS.length; i++) { + var keyword = PGListerals.KEYWORDS[i]; + if (match (keyword, prefix)) { + candidates.append (new Candidate (PGListerals.KEYWORDS[i], "keyword")); + } + } + + return candidates; + } + + private List suggest_columns (string prefix = "") { + + var candidates = new List (); + columns.foreach ((item) => { + if (match (item, prefix)) { + var cand = new Candidate (item, "column"); + candidates.append (cand); + } + }); + + return (owned)candidates; + } + + private List suggest_schemas (string prefix = "") { + + var candidates = new List (); + schemas.foreach ((item) => { + if (match (item, prefix)) { + var cand = new Candidate (item, "schema"); + candidates.append (cand); + } + }); + + return (owned)candidates; + } + + private List suggest_tables (string prefix = "") { + + var candidates = new List (); + tables.foreach ((item) => { + if (match (item, prefix)) { + var cand = new Candidate (item, "table"); + candidates.append (cand); + } + }); + + return (owned)candidates; + } + + private bool match (string text, string needle) { + return text.up ().has_prefix (needle.up ()); + } + + private void refresh_data (Schema schema) { + refresh_columns (); + } + + private void refresh_columns () { + var query = new Query (COLUMN_LIST); + sql_service.exec_query.begin (query, (obj, res) => { + try { + var relation = sql_service.exec_query_params.end (res); + // avoid dupplicates + var tmp_schemas = new Tree ((a, b) => { return strcmp (a, b); }); + var tmp_tables = new Tree ((a, b) => { return strcmp (a, b); }); + var tmp_columns = new Tree ((a, b) => { return strcmp (a, b); }); + + foreach (var row in relation) { + tmp_schemas.insert (row[0], row[0]); + tmp_tables.insert (row[1], row[1]); + tmp_columns.insert (row[2], row[2]); + } + schemas = new List (); + tmp_schemas.foreach ((key, val) => { + schemas.append (val.dup ()); + return false; + }); + + tables = new List (); + tmp_tables.foreach ((key, val) => { + tables.append (val.dup ()); + return false; + }); + + columns = new List (); + tmp_columns.foreach ((key, val) => { + columns.append (val.dup ()); + + return false; + }); + + } catch (PsequelError err) { + debug (err.message); + } + }); + } + + const string COLUMN_LIST = """SELECT table_schema, table_name, column_name + FROM information_schema.columns"""; + } + + + public class Candidate : Object, GtkSource.CompletionProposal { + public string value { get; set; } + public string group { get; set; } + + public Candidate (string value, string group) { + this.value = value; + this.group = group; + } + } + + public class SchemaContext : Object { + + } + + public class PGListerals { + public const string[] KEYWORDS = { "ADD", "ADD CONSTRAINT", "ALL", "ALTER", "ALTER COLUMN", "ALTER TABLE", "AND", "ANY", "AS", "ASC", "BACKUP DATABASE", "BETWEEN", "CASE", "CHECK", "COLUMN", "CONSTRAINT", "CREATE", "CREATE DATABASE", "CREATE INDEX", "CREATE OR REPLACE VIEW", "CREATE TABLE", "CREATE PROCEDURE", "CREATE UNIQUE INDEX", "CREATE VIEW", "DATABASE", "DEFAULT", "DELETE", "DESC", "DISTINCT", "DROP", "DROP COLUMN", "DROP CONSTRAINT", "DROP DATABASE", "DROP DEFAULT", "DROP INDEX", "DROP TABLE", "DROP VIEW", "EXEC", "EXISTS", "FOREIGN KEY", "FROM", "FULL OUTER JOIN", "GROUP BY", "HAVING", "IN", "INDEX", "INNER JOIN", "INSERT INTO", "IS NULL", "IS NOT NULL", "JOIN", "LEFT JOIN", "LIKE", "LIMIT", "NOT", "NOT NULL", "OR", "ORDER BY", "OUTER JOIN", "PRIMARY KEY", "PROCEDURE", "RIGHT JOIN", "ROWNUM", "SELECT", "SELECT DISTINCT", "SELECT INTO", "SELECT TOP", "SET", "TABLE", "TOP", "TRUNCATE TABLE", "UNION", "UNION ALL", "UNIQUE", "UPDATE", "VALUES", "VIEW", "WHERE"}; + public const string[] FUNCTIONS = { "ABBREV", "ABS", "AGE", "AREA", "ARRAY_AGG", "ARRAY_APPEND", "ARRAY_CAT", "ARRAY_DIMS", "ARRAY_FILL", "ARRAY_LENGTH", "ARRAY_LOWER", "ARRAY_NDIMS", "ARRAY_POSITION", "ARRAY_POSITIONS", "ARRAY_PREPEND", "ARRAY_REMOVE", "ARRAY_REPLACE", "ARRAY_TO_STRING", "ARRAY_UPPER", "ASCII", "AVG", "BIT_AND", "BIT_LENGTH", "BIT_OR", "BOOL_AND", "BOOL_OR", "BOUND_BOX", "BOX", "BROADCAST", "BTRIM", "CARDINALITY", "CBRT", "CEIL", "CEILING", "CENTER", "CHAR_LENGTH", "CHR", "CIRCLE", "CLOCK_TIMESTAMP", "CONCAT", "CONCAT_WS", "CONVERT", "CONVERT_FROM", "CONVERT_TO", "COUNT", "CUME_DIST", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATE_PART", "DATE_TRUNC", "DECODE", "DEGREES", "DENSE_RANK", "DIAMETER", "DIV", "ENCODE", "ENUM_FIRST", "ENUM_LAST", "ENUM_RANGE", "EVERY", "EXP", "EXTRACT", "FAMILY", "FIRST_VALUE", "FLOOR", "FORMAT", "GET_BIT", "GET_BYTE", "HEIGHT", "HOST", "HOSTMASK", "INET_MERGE", "INET_SAME_FAMILY", "INITCAP", "ISCLOSED", "ISFINITE", "ISOPEN", "JUSTIFY_DAYS", "JUSTIFY_HOURS", "JUSTIFY_INTERVAL", "LAG", "LAST_VALUE", "LEAD", "LEFT", "LENGTH", "LINE", "LN", "LOCALTIME", "LOCALTIMESTAMP", "LOG", "LOG10", "LOWER", "LPAD", "LSEG", "LTRIM", "MAKE_DATE", "MAKE_INTERVAL", "MAKE_TIME", "MAKE_TIMESTAMP", "MAKE_TIMESTAMPTZ", "MASKLEN", "MAX", "MD5", "MIN", "MOD", "NETMASK", "NETWORK", "NOW", "NPOINTS", "NTH_VALUE", "NTILE", "NUM_NONNULLS", "NUM_NULLS", "OCTET_LENGTH", "OVERLAY", "PARSE_IDENT", "PATH", "PCLOSE", "PERCENT_RANK", "PG_CLIENT_ENCODING", "PI", "POINT", "POLYGON", "POPEN", "POSITION", "POWER", "QUOTE_IDENT", "QUOTE_LITERAL", "QUOTE_NULLABLE", "RADIANS", "RADIUS", "RANDOM", "RANK", "REGEXP_MATCH", "REGEXP_MATCHES", "REGEXP_REPLACE", "REGEXP_SPLIT_TO_ARRAY", "REGEXP_SPLIT_TO_TABLE", "REPEAT", "REPLACE", "REVERSE", "RIGHT", "ROUND", "ROW_NUMBER", "RPAD", "RTRIM", "SCALE", "SET_BIT", "SET_BYTE", "SET_MASKLEN", "SHA224", "SHA256", "SHA384", "SHA512", "SIGN", "SPLIT_PART", "SQRT", "STARTS_WITH", "STATEMENT_TIMESTAMP", "STRING_TO_ARRAY", "STRPOS", "SUBSTR", "SUBSTRING", "SUM", "TEXT", "TIMEOFDAY", "TO_ASCII", "TO_CHAR", "TO_DATE", "TO_HEX", "TO_NUMBER", "TO_TIMESTAMP", "TRANSACTION_TIMESTAMP", "TRANSLATE", "TRIM", "TRUNC", "UNNEST", "UPPER", "WIDTH", "WIDTH_BUCKET", "XMLAGG" }; + public const string[] DATATYPES = { "ANY", "ANYARRAY", "ANYELEMENT", "ANYENUM", "ANYNONARRAY", "ANYRANGE", "BIGINT", "BIGSERIAL", "BIT", "BIT VARYING", "BOOL", "BOOLEAN", "BOX", "BYTEA", "CHAR", "CHARACTER", "CHARACTER VARYING", "CIDR", "CIRCLE", "CSTRING", "DATE", "DECIMAL", "DOUBLE PRECISION", "EVENT_TRIGGER", "FDW_HANDLER", "FLOAT4", "FLOAT8", "INET", "INT", "INT2", "INT4", "INT8", "INTEGER", "INTERNAL", "INTERVAL", "JSON", "JSONB", "LANGUAGE_HANDLER", "LINE", "LSEG", "MACADDR", "MACADDR8", "MONEY", "NUMERIC", "OID", "OPAQUE", "PATH", "PG_LSN", "POINT", "POLYGON", "REAL", "RECORD", "REGCLASS", "REGCONFIG", "REGDICTIONARY", "REGNAMESPACE", "REGOPER", "REGOPERATOR", "REGPROC", "REGPROCEDURE", "REGROLE", "REGTYPE", "SERIAL", "SERIAL2", "SERIAL4", "SERIAL8", "SMALLINT", "SMALLSERIAL", "TEXT", "TIME", "TIMESTAMP", "TRIGGER", "TSQUERY", "TSVECTOR", "TXID_SNAPSHOT", "UUID", "VARBIT", "VARCHAR", "VOID", "XML" }; + public const string[] RESERVED = { "ALL", "ANALYSE", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", "ASYMMETRIC", "BOTH", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "CONSTRAINT", "CREATE", "CURRENT_CATALOG", "CURRENT_DATE", "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "DEFAULT", "DEFERRABLE", "DESC", "DISTINCT", "DO", "ELSE", "END", "EXCEPT", "FALSE", "FETCH", "FOR", "FOREIGN", "FROM", "GRANT", "GROUP", "HAVING", "IN", "INITIALLY", "INTERSECT", "INTO", "LATERAL", "LEADING", "LIMIT", "LOCALTIME", "LOCALTIMESTAMP", "NOT", "NULL", "OFFSET", "ON", "ONLY", "OR", "ORDER", "PLACING", "PRIMARY", "REFERENCES", "RETURNING", "SELECT", "SESSION_USER", "SOME", "SYMMETRIC", "TABLE", "THEN", "TO", "TRAILING", "TRUE", "UNION", "UNIQUE", "USER", "USING", "VARIADIC", "WHEN", "WHERE", "WINDOW", "WITH", "AUTHORIZATION", "BINARY", "COLLATION", "CONCURRENTLY", "CROSS", "CURRENT_SCHEMA", "FREEZE", "FULL", "ILIKE", "INNER", "IS", "ISNULL", "JOIN", "LEFT", "LIKE", "NATURAL", "NOTNULL", "OUTER", "OVERLAPS", "RIGHT", "SIMILAR", "TABLESAMPLE", "VERBOSE" }; + } +} \ No newline at end of file diff --git a/src/services/SQLCompletionProvider.vala b/src/services/SQLCompletionProvider.vala deleted file mode 100644 index bfcdda4..0000000 --- a/src/services/SQLCompletionProvider.vala +++ /dev/null @@ -1,169 +0,0 @@ -namespace Psequel { - public class SQLCompletionProvider : Object, GtkSource.CompletionProvider { - - const string[] KEYWORDS = { "A", "ABORT", "ABS", "ABSENT", "ABSOLUTE", "ACCESS", "ACCORDING", "ACOS", "ACTION", "ADA", "ADD", "ADMIN", "AFTER", "AGGREGATE", "ALL", "ALLOCATE", "ALSO", "ALTER", "ALWAYS", "ANALYSE", "ANALYZE", "AND", "ANY", "ARE", "ARRAY", "ARRAY_AGG", "ARRAY_MAX_CARDINALITY", "AS", "ASC", "ASENSITIVE", "ASIN", "ASSERTION", "ASSIGNMENT", "ASYMMETRIC", "AT", "ATAN", "ATOMIC", "ATTACH", "ATTRIBUTE", "ATTRIBUTES", "AUTHORIZATION", "AVG", "BACKWARD", "BASE64", "BEFORE", "BEGIN", "BEGIN_FRAME", "BEGIN_PARTITION", "BERNOULLI", "BETWEEN", "BIGINT", "BINARY", "BIT", "BIT_LENGTH", "BLOB", "BLOCKED", "BOM", "BOOLEAN", "BOTH", "BREADTH", "BY", "C", "CACHE", "CALL", "CALLED", "CARDINALITY", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CATALOG_NAME", "CEIL", "CEILING", "CHAIN", "CHAINING", "CHAR", "CHARACTER", "CHARACTERISTICS", "CHARACTERS", "CHARACTER_LENGTH", "CHARACTER_SET_CATALOG", "CHARACTER_SET_NAME", "CHARACTER_SET_SCHEMA", "CHAR_LENGTH", "CHECK", "CHECKPOINT", "CLASS", "CLASSIFIER", "CLASS_ORIGIN", "CLOB", "CLOSE", "CLUSTER", "COALESCE", "COBOL", "COLLATE", "COLLATION", "COLLATION_CATALOG", "COLLATION_NAME", "COLLATION_SCHEMA", "COLLECT", "COLUMN", "COLUMNS", "COLUMN_NAME", "COMMAND_FUNCTION", "COMMAND_FUNCTION_CODE", "COMMENT", "COMMENTS", "COMMIT", "COMMITTED", "COMPRESSION", "CONCURRENTLY", "CONDITION", "CONDITIONAL", "CONDITION_NUMBER", "CONFIGURATION", "CONFLICT", "CONNECT", "CONNECTION", "CONNECTION_NAME", "CONSTRAINT", "CONSTRAINTS", "CONSTRAINT_CATALOG", "CONSTRAINT_NAME", "CONSTRAINT_SCHEMA", "CONSTRUCTOR", "CONTAINS", "CONTENT", "CONTINUE", "CONTROL", "CONVERSION", "CONVERT", "COPY", "CORR", "CORRESPONDING", "COS", "COSH", "COST", "COUNT", "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CSV", "CUBE", "CUME_DIST", "CURRENT", "CURRENT_CATALOG", "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_ROW", "CURRENT_SCHEMA", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CURSOR_NAME", "CYCLE", "DATA", "DATABASE", "DATALINK", "DATE", "DATETIME_INTERVAL_CODE", "DATETIME_INTERVAL_PRECISION", "DAY", "DB", "DEALLOCATE", "DEC", "DECFLOAT", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULTS", "DEFERRABLE", "DEFERRED", "DEFINE", "DEFINED", "DEFINER", "DEGREE", "DELETE", "DELIMITER", "DELIMITERS", "DENSE_RANK", "DEPENDS", "DEPTH", "DEREF", "DERIVED", "DESC", "DESCRIBE", "DESCRIPTOR", "DETACH", "DETERMINISTIC", "DIAGNOSTICS", "DICTIONARY", "DISABLE", "DISCARD", "DISCONNECT", "DISPATCH", "DISTINCT", "DLNEWCOPY", "DLPREVIOUSCOPY", "DLURLCOMPLETE", "DLURLCOMPLETEONLY", "DLURLCOMPLETEWRITE", "DLURLPATH", "DLURLPATHONLY", "DLURLPATHWRITE", "DLURLSCHEME", "DLURLSERVER", "DLVALUE", "DO", "DOCUMENT", "DOMAIN", "DOUBLE", "DROP", "DYNAMIC", "DYNAMIC_FUNCTION", "DYNAMIC_FUNCTION_CODE", "EACH", "ELEMENT", "ELSE", "EMPTY", "ENABLE", "ENCODING", "ENCRYPTED", "END", "END-EXEC", "END_FRAME", "END_PARTITION", "ENFORCED", "ENUM", "EQUALS", "ERROR", "ESCAPE", "EVENT", "EVERY", "EXCEPT", "EXCEPTION", "EXCLUDE", "EXCLUDING", "EXCLUSIVE", "EXEC", "EXECUTE", "EXISTS", "EXP", "EXPLAIN", "EXPRESSION", "EXTENSION", "EXTERNAL", "EXTRACT", "FALSE", "FAMILY", "FETCH", "FILE", "FILTER", "FINAL", "FINALIZE", "FINISH", "FIRST", "FIRST_VALUE", "FLAG", "FLOAT", "FLOOR", "FOLLOWING", "FOR", "FORCE", "FOREIGN", "FORMAT", "FORTRAN", "FORWARD", "FOUND", "FRAME_ROW", "FREE", "FREEZE", "FROM", "FS", "FULFILL", "FULL", "FUNCTION", "FUNCTIONS", "FUSION", "G", "GENERAL", "GENERATED", "GET", "GLOBAL", "GO", "GOTO", "GRANT", "GRANTED", "GREATEST", "GROUP", "GROUPING", "GROUPS", "HANDLER", "HAVING", "HEADER", "HEX", "HIERARCHY", "HOLD", "HOUR", "ID", "IDENTITY", "IF", "IGNORE", "ILIKE", "IMMEDIATE", "IMMEDIATELY", "IMMUTABLE", "IMPLEMENTATION", "IMPLICIT", "IMPORT", "IN", "INCLUDE", "INCLUDING", "INCREMENT", "INDENT", "INDEX", "INDEXES", "INDICATOR", "INHERIT", "INHERITS", "INITIAL", "INITIALLY", "INLINE", "INNER", "INOUT", "INPUT", "INSENSITIVE", "INSERT", "INSTANCE", "INSTANTIABLE", "INSTEAD", "INT", "INTEGER", "INTEGRITY", "INTERSECT", "INTERSECTION", "INTERVAL", "INTO", "INVOKER", "IS", "ISNULL", "ISOLATION", "JOIN", "JSON_ARRAY", "JSON_ARRAYAGG", "JSON_EXISTS", "JSON_OBJECT", "JSON_OBJECTAGG", "JSON_QUERY", "JSON_TABLE", "JSON_TABLE_PRIMITIVE", "JSON_VALUE", "K", "KEEP", "KEY", "KEYS", "KEY_MEMBER", "KEY_TYPE", "LABEL", "LAG", "LANGUAGE", "LARGE", "LAST", "LAST_VALUE", "LATERAL", "LEAD", "LEADING", "LEAKPROOF", "LEAST", "LEFT", "LENGTH", "LEVEL", "LIBRARY", "LIKE", "LIKE_REGEX", "LIMIT", "LINK", "LISTAGG", "LISTEN", "LN", "LOAD", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCATION", "LOCATOR", "LOCK", "LOCKED", "LOG", "LOG10", "LOGGED", "LOWER", "M", "MAP", "MAPPING", "MATCH", "MATCHED", "MATCHES", "MATCH_NUMBER", "MATCH_RECOGNIZE", "MATERIALIZED", "MAX", "MAXVALUE", "MEASURES", "MEMBER", "MERGE", "MESSAGE_LENGTH", "MESSAGE_OCTET_LENGTH", "MESSAGE_TEXT", "METHOD", "MIN", "MINUTE", "MINVALUE", "MOD", "MODE", "MODIFIES", "MODULE", "MONTH", "MORE", "MOVE", "MULTISET", "MUMPS", "NAME", "NAMES", "NAMESPACE", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NESTED", "NESTING", "NEW", "NEXT", "NFC", "NFD", "NFKC", "NFKD", "NIL", "NO", "NONE", "NORMALIZE", "NORMALIZED", "NOT", "NOTHING", "NOTIFY", "NOTNULL", "NOWAIT", "NTH_VALUE", "NTILE", "NULL", "NULLABLE", "NULLIF", "NULLS", "NULL_ORDERING", "NUMBER", "NUMERIC", "OBJECT", "OCCURRENCE", "OCCURRENCES_REGEX", "OCTETS", "OCTET_LENGTH", "OF", "OFF", "OFFSET", "OIDS", "OLD", "OMIT", "ON", "ONE", "ONLY", "OPEN", "OPERATOR", "OPTION", "OPTIONS", "OR", "ORDER", "ORDERING", "ORDINALITY", "OTHERS", "OUT", "OUTER", "OUTPUT", "OVER", "OVERFLOW", "OVERLAPS", "OVERLAY", "OVERRIDING", "OWNED", "OWNER", "P", "PAD", "PARALLEL", "PARAMETER", "PARAMETER_MODE", "PARAMETER_NAME", "PARAMETER_ORDINAL_POSITION", "PARAMETER_SPECIFIC_CATALOG", "PARAMETER_SPECIFIC_NAME", "PARAMETER_SPECIFIC_SCHEMA", "PARSER", "PARTIAL", "PARTITION", "PASCAL", "PASS", "PASSING", "PASSTHROUGH", "PASSWORD", "PAST", "PATH", "PATTERN", "PER", "PERCENT", "PERCENTILE_CONT", "PERCENTILE_DISC", "PERCENT_RANK", "PERIOD", "PERMISSION", "PERMUTE", "PIPE", "PLACING", "PLAN", "PLANS", "PLI", "POLICY", "PORTION", "POSITION", "POSITION_REGEX", "POWER", "PRECEDES", "PRECEDING", "PRECISION", "PREPARE", "PREPARED", "PRESERVE", "PREV", "PRIMARY", "PRIOR", "PRIVATE", "PRIVILEGES", "PROCEDURAL", "PROCEDURE", "PROCEDURES", "PROGRAM", "PRUNE", "PTF", "PUBLIC", "PUBLICATION", "QUOTE", "QUOTES", "RANGE", "RANK", "READ", "READS", "REAL", "REASSIGN", "RECHECK", "RECOVERY", "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REFRESH", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY", "REINDEX", "RELATIVE", "RELEASE", "RENAME", "REPEATABLE", "REPLACE", "REPLICA", "REQUIRING", "RESET", "RESPECT", "RESTART", "RESTORE", "RESTRICT", "RESULT", "RETURN", "RETURNED_CARDINALITY", "RETURNED_LENGTH", "RETURNED_OCTET_LENGTH", "RETURNED_SQLSTATE", "RETURNING", "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUTINE", "ROUTINES", "ROUTINE_CATALOG", "ROUTINE_NAME", "ROUTINE_SCHEMA", "ROW", "ROWS", "ROW_COUNT", "ROW_NUMBER", "RULE", "RUNNING", "SAVEPOINT", "SCALAR", "SCALE", "SCHEMA", "SCHEMAS", "SCHEMA_NAME", "SCOPE", "SCOPE_CATALOG", "SCOPE_NAME", "SCOPE_SCHEMA", "SCROLL", "SEARCH", "SECOND", "SECTION", "SECURITY", "SEEK", "SELECT", "SELECTIVE", "SELF", "SEMANTICS", "SENSITIVE", "SEQUENCE", "SEQUENCES", "SERIALIZABLE", "SERVER", "SERVER_NAME", "SESSION", "SESSION_USER", "SET", "SETOF", "SETS", "SHARE", "SHOW", "SIMILAR", "SIMPLE", "SIN", "SINH", "SIZE", "SKIP", "SMALLINT", "SNAPSHOT", "SOME", "SORT_DIRECTION", "SOURCE", "SPACE", "SPECIFIC", "SPECIFICTYPE", "SPECIFIC_NAME", "SQL", "SQLCODE", "SQLERROR", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "SQRT", "STABLE", "STANDALONE", "START", "STATE", "STATEMENT", "STATIC", "STATISTICS", "STDDEV_POP", "STDDEV_SAMP", "STDIN", "STDOUT", "STORAGE", "STORED", "STRICT", "STRING", "STRIP", "STRUCTURE", "STYLE", "SUBCLASS_ORIGIN", "SUBMULTISET", "SUBSCRIPTION", "SUBSET", "SUBSTRING", "SUBSTRING_REGEX", "SUCCEEDS", "SUM", "SUPPORT", "SYMMETRIC", "SYSID", "SYSTEM", "SYSTEM_TIME", "SYSTEM_USER", "T", "TABLE", "TABLES", "TABLESAMPLE", "TABLESPACE", "TABLE_NAME", "TAN", "TANH", "TEMP", "TEMPLATE", "TEMPORARY", "TEXT", "THEN", "THROUGH", "TIES", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TOKEN", "TOP_LEVEL_COUNT", "TRAILING", "TRANSACTION", "TRANSACTIONS_COMMITTED", "TRANSACTIONS_ROLLED_BACK", "TRANSACTION_ACTIVE", "TRANSFORM", "TRANSFORMS", "TRANSLATE", "TRANSLATE_REGEX", "TRANSLATION", "TREAT", "TRIGGER", "TRIGGER_CATALOG", "TRIGGER_NAME", "TRIGGER_SCHEMA", "TRIM", "TRIM_ARRAY", "TRUE", "TRUNCATE", "TRUSTED", "TYPE", "TYPES", "UESCAPE", "UNBOUNDED", "UNCOMMITTED", "UNCONDITIONAL", "UNDER", "UNENCRYPTED", "UNION", "UNIQUE", "UNKNOWN", "UNLINK", "UNLISTEN", "UNLOGGED", "UNMATCHED", "UNNAMED", "UNNEST", "UNTIL", "UNTYPED", "UPDATE", "UPPER", "URI", "USAGE", "USER", "USER_DEFINEDTYPE_CATALOG", "USER_DEFINED_TYPE_CODE", "USER_DEFINED_TYPE_NAME", "USER_DEFINED_TYPE_SCHEMA", "USING", "UTF16", "UTF32", "UTF8", "VACUUM", "VALID", "VALIDATE", "VALIDATOR", "VALUE", "VALUES", "VALUE_OF", "VARBINARY", "VARCHAR", "VARIADIC", "VARYING", "VAR_POP", "VAR_SAMP", "VERBOSE", "VERSION", "VERSIONING", "VIEW", "VIEWS", "VOLATILE", "WHEN", "WHENEVER", "WHERE", "WHITESPACE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WORK", "WRAPPER", "WRITE", "XML", "XMLAGG", "XMLATTRIBUTES", "XMLBINARY", "XMLCAST", "XMLCOMMENT", "XMLCONCAT", "XMLDECLARATION", "XMLDOCUMENT", "XMLELEMENT", "XMLEXISTS", "XMLFOREST", "XMLITERATE", "XMLNAMESPACES", "XMLPARSE", "XMLPI", "XMLQUERY", "XMLROOT", "XMLSCHEMA", "XMLSERIALIZE", "XMLTABLE", "XMLTEXT", "XMLVALIDATE", "YEAR", "YES", "ZONE" }; - - private List static_candidates; - private List dynamic_candidates; - private Gtk.FilterListModel model; - private Gtk.StringFilter filter; - - - private SchemaViewModel schema_viewmodel; - public SQLCompletionProvider () { - base (); - debug ("SQLCompletionProvider"); - this.schema_viewmodel = autowire (); - - static_candidates = new List (); - for (int i = 0; i < PGListerals.KEYWORDS.length; i++) { - static_candidates.append (new Model (PGListerals.KEYWORDS[i], "KEYWORD")); - } - - for (int i = 0; i < PGListerals.FUNCTIONS.length; i++) { - static_candidates.append (new Model (PGListerals.FUNCTIONS[i], "FUNCTION")); - } - - for (int i = 0; i < PGListerals.DATATYPES.length; i++) { - static_candidates.append (new Model (PGListerals.DATATYPES[i], "DATATYPE")); - } - - for (int i = 0; i < PGListerals.RESERVED.length; i++) { - static_candidates.append (new Model (PGListerals.RESERVED[i], "RESERVED")); - } - - // this.notify["query-viewmodel"].connect (() => { - - // dynamic_candidates = new List (); - // query_viewmodel.current_schema.tables.foreach ((table) => { - // dynamic_candidates.append (new Model (table.name, "TABLE")); - // }); - // query_viewmodel.current_schema.views.foreach ((view) => { - // dynamic_candidates.append (new Model (view.name, "VIEW")); - // }); - // }); - - var expression = new Gtk.PropertyExpression (typeof (Model), null, "value"); - filter = new Gtk.StringFilter (expression); - filter.match_mode = Gtk.StringFilterMatchMode.PREFIX; - - model = new Gtk.FilterListModel (null, filter); - } - - public void activate (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal) { - debug ("activate 1"); - - var model = (Model) proposal; - var buf = context.get_buffer (); - Gtk.TextIter start, end; - - last_word (buf, out start, out end); - - buf.delete_range (start, end); - buf.insert_at_cursor (model.value, model.value.length); - // buf.insert (ref iter, model.value, model.value.length); - } - - public void display (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal, GtkSource.CompletionCell cell) { - - var model = (Model) proposal; - - // cell.text = cell.column.to_string (); - - switch (cell.column) { - // name - case GtkSource.CompletionColumn.TYPED_TEXT: - cell.text = model.value; - break; - - // group - case GtkSource.CompletionColumn.AFTER: - cell.text = model.group; - break; - // case GtkSource.CompletionColumn.DETAILS: - // cell.text = "DETAILS"; - // break; - // case GtkSource.CompletionColumn.COMMENT: - // cell.text = "COMMENT"; - // break; - // case GtkSource.CompletionColumn.ICON: - // cell.text = "ICON"; - // break; - // case GtkSource.CompletionColumn.BEFORE: - // cell.text = "BEFORE"; - // break; - default: break; - } - } - - public async GLib.ListModel populate_async (GtkSource.CompletionContext context, GLib.Cancellable? cancellable) { - - /* - Query viewmodel is not set until the query view is created. - */ - // yield schema_viewmodel.load_schema (schema_viewmodel.current_schema); - dynamic_candidates = new List (); - // schema_viewmodel.current_schema.tables.foreach ((table) => { - // dynamic_candidates.append (new Model (table.name, "TABLE")); - // }); - - // schema_viewmodel.current_schema.views.foreach ((view) => { - // dynamic_candidates.append (new Model (view.name, "VIEW")); - // }); - - var candidates = new ObservableList (); - candidates.append_all (static_candidates); - candidates.append_all (dynamic_candidates); - model.model = candidates; - - - var word = last_word (context.get_buffer ()); - filter.search = word; - debug ("populate_async: %s", word); - debug ("size: %u", model.get_n_items ()); - - return model; - } - - public void refilter (GtkSource.CompletionContext context, GLib.ListModel _model) { - - debug ("refilter"); - - var word = last_word (context.get_buffer ()); - filter.search = word; - filter.changed (Gtk.FilterChange.MORE_STRICT); - debug ("populate_async: %s", word); - } - - public class Model : Object, GtkSource.CompletionProposal { - public string value { get; set; } - public string group { get; set; } - - public Model (string value, string group) { - this.value = value; - this.group = group; - } - } - - private string last_word (GtkSource.Buffer buf, out Gtk.TextIter start = null, out Gtk.TextIter end = null) { - Gtk.TextIter iter; - buf.get_iter_at_offset (out iter, buf.cursor_position); - - start = iter; - end = iter; - - start.backward_word_start (); - end.forward_word_end (); - - return buf.get_text (start, end, false); - } - } - - - public class PGListerals { - public const string[] KEYWORDS = { "ADD", "ADD CONSTRAINT", "ALL", "ALTER", "ALTER COLUMN", "ALTER TABLE", "AND", "ANY", "AS", "ASC", "BACKUP DATABASE", "BETWEEN", "CASE", "CHECK", "COLUMN", "CONSTRAINT", "CREATE", "CREATE DATABASE", "CREATE INDEX", "CREATE OR REPLACE VIEW", "CREATE TABLE", "CREATE PROCEDURE", "CREATE UNIQUE INDEX", "CREATE VIEW", "DATABASE", "DEFAULT", "DELETE", "DESC", "DISTINCT", "DROP", "DROP COLUMN", "DROP CONSTRAINT", "DROP DATABASE", "DROP DEFAULT", "DROP INDEX", "DROP TABLE", "DROP VIEW", "EXEC", "EXISTS", "FOREIGN KEY", "FROM", "FULL OUTER JOIN", "GROUP BY", "HAVING", "IN", "INDEX", "INNER JOIN", "INSERT INTO", "INSERT INTO SELECT", "IS NULL", "IS NOT NULL", "JOIN", "LEFT JOIN", "LIKE", "LIMIT", "NOT", "NOT NULL", "OR", "ORDER BY", "OUTER JOIN", "PRIMARY KEY", "PROCEDURE", "RIGHT JOIN", "ROWNUM", "SELECT", "SELECT DISTINCT", "SELECT INTO", "SELECT TOP", "SET", "TABLE", "TOP", "TRUNCATE TABLE", "UNION", "UNION ALL", "UNIQUE", "UPDATE", "VALUES", "VIEW", "WHERE" }; - public const string[] FUNCTIONS = { "ABBREV", "ABS", "AGE", "AREA", "ARRAY_AGG", "ARRAY_APPEND", "ARRAY_CAT", "ARRAY_DIMS", "ARRAY_FILL", "ARRAY_LENGTH", "ARRAY_LOWER", "ARRAY_NDIMS", "ARRAY_POSITION", "ARRAY_POSITIONS", "ARRAY_PREPEND", "ARRAY_REMOVE", "ARRAY_REPLACE", "ARRAY_TO_STRING", "ARRAY_UPPER", "ASCII", "AVG", "BIT_AND", "BIT_LENGTH", "BIT_OR", "BOOL_AND", "BOOL_OR", "BOUND_BOX", "BOX", "BROADCAST", "BTRIM", "CARDINALITY", "CBRT", "CEIL", "CEILING", "CENTER", "CHAR_LENGTH", "CHR", "CIRCLE", "CLOCK_TIMESTAMP", "CONCAT", "CONCAT_WS", "CONVERT", "CONVERT_FROM", "CONVERT_TO", "COUNT", "CUME_DIST", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATE_PART", "DATE_TRUNC", "DECODE", "DEGREES", "DENSE_RANK", "DIAMETER", "DIV", "ENCODE", "ENUM_FIRST", "ENUM_LAST", "ENUM_RANGE", "EVERY", "EXP", "EXTRACT", "FAMILY", "FIRST_VALUE", "FLOOR", "FORMAT", "GET_BIT", "GET_BYTE", "HEIGHT", "HOST", "HOSTMASK", "INET_MERGE", "INET_SAME_FAMILY", "INITCAP", "ISCLOSED", "ISFINITE", "ISOPEN", "JUSTIFY_DAYS", "JUSTIFY_HOURS", "JUSTIFY_INTERVAL", "LAG", "LAST_VALUE", "LEAD", "LEFT", "LENGTH", "LINE", "LN", "LOCALTIME", "LOCALTIMESTAMP", "LOG", "LOG10", "LOWER", "LPAD", "LSEG", "LTRIM", "MAKE_DATE", "MAKE_INTERVAL", "MAKE_TIME", "MAKE_TIMESTAMP", "MAKE_TIMESTAMPTZ", "MASKLEN", "MAX", "MD5", "MIN", "MOD", "NETMASK", "NETWORK", "NOW", "NPOINTS", "NTH_VALUE", "NTILE", "NUM_NONNULLS", "NUM_NULLS", "OCTET_LENGTH", "OVERLAY", "PARSE_IDENT", "PATH", "PCLOSE", "PERCENT_RANK", "PG_CLIENT_ENCODING", "PI", "POINT", "POLYGON", "POPEN", "POSITION", "POWER", "QUOTE_IDENT", "QUOTE_LITERAL", "QUOTE_NULLABLE", "RADIANS", "RADIUS", "RANDOM", "RANK", "REGEXP_MATCH", "REGEXP_MATCHES", "REGEXP_REPLACE", "REGEXP_SPLIT_TO_ARRAY", "REGEXP_SPLIT_TO_TABLE", "REPEAT", "REPLACE", "REVERSE", "RIGHT", "ROUND", "ROW_NUMBER", "RPAD", "RTRIM", "SCALE", "SET_BIT", "SET_BYTE", "SET_MASKLEN", "SHA224", "SHA256", "SHA384", "SHA512", "SIGN", "SPLIT_PART", "SQRT", "STARTS_WITH", "STATEMENT_TIMESTAMP", "STRING_TO_ARRAY", "STRPOS", "SUBSTR", "SUBSTRING", "SUM", "TEXT", "TIMEOFDAY", "TO_ASCII", "TO_CHAR", "TO_DATE", "TO_HEX", "TO_NUMBER", "TO_TIMESTAMP", "TRANSACTION_TIMESTAMP", "TRANSLATE", "TRIM", "TRUNC", "UNNEST", "UPPER", "WIDTH", "WIDTH_BUCKET", "XMLAGG" }; - public const string[] DATATYPES = { "ANY", "ANYARRAY", "ANYELEMENT", "ANYENUM", "ANYNONARRAY", "ANYRANGE", "BIGINT", "BIGSERIAL", "BIT", "BIT VARYING", "BOOL", "BOOLEAN", "BOX", "BYTEA", "CHAR", "CHARACTER", "CHARACTER VARYING", "CIDR", "CIRCLE", "CSTRING", "DATE", "DECIMAL", "DOUBLE PRECISION", "EVENT_TRIGGER", "FDW_HANDLER", "FLOAT4", "FLOAT8", "INET", "INT", "INT2", "INT4", "INT8", "INTEGER", "INTERNAL", "INTERVAL", "JSON", "JSONB", "LANGUAGE_HANDLER", "LINE", "LSEG", "MACADDR", "MACADDR8", "MONEY", "NUMERIC", "OID", "OPAQUE", "PATH", "PG_LSN", "POINT", "POLYGON", "REAL", "RECORD", "REGCLASS", "REGCONFIG", "REGDICTIONARY", "REGNAMESPACE", "REGOPER", "REGOPERATOR", "REGPROC", "REGPROCEDURE", "REGROLE", "REGTYPE", "SERIAL", "SERIAL2", "SERIAL4", "SERIAL8", "SMALLINT", "SMALLSERIAL", "TEXT", "TIME", "TIMESTAMP", "TRIGGER", "TSQUERY", "TSVECTOR", "TXID_SNAPSHOT", "UUID", "VARBIT", "VARCHAR", "VOID", "XML" }; - public const string[] RESERVED = { "ALL", "ANALYSE", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", "ASYMMETRIC", "BOTH", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "CONSTRAINT", "CREATE", "CURRENT_CATALOG", "CURRENT_DATE", "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "DEFAULT", "DEFERRABLE", "DESC", "DISTINCT", "DO", "ELSE", "END", "EXCEPT", "FALSE", "FETCH", "FOR", "FOREIGN", "FROM", "GRANT", "GROUP", "HAVING", "IN", "INITIALLY", "INTERSECT", "INTO", "LATERAL", "LEADING", "LIMIT", "LOCALTIME", "LOCALTIMESTAMP", "NOT", "NULL", "OFFSET", "ON", "ONLY", "OR", "ORDER", "PLACING", "PRIMARY", "REFERENCES", "RETURNING", "SELECT", "SESSION_USER", "SOME", "SYMMETRIC", "TABLE", "THEN", "TO", "TRAILING", "TRUE", "UNION", "UNIQUE", "USER", "USING", "VARIADIC", "WHEN", "WHERE", "WINDOW", "WITH", "AUTHORIZATION", "BINARY", "COLLATION", "CONCURRENTLY", "CROSS", "CURRENT_SCHEMA", "FREEZE", "FULL", "ILIKE", "INNER", "IS", "ISNULL", "JOIN", "LEFT", "LIKE", "NATURAL", "NOTNULL", "OUTER", "OVERLAPS", "RIGHT", "SIMILAR", "TABLESAMPLE", "VERBOSE" }; - } -} \ No newline at end of file diff --git a/src/services/SchemaService.vala b/src/services/SchemaService.vala index c5b594d..a82f44d 100644 --- a/src/services/SchemaService.vala +++ b/src/services/SchemaService.vala @@ -4,39 +4,6 @@ namespace Psequel { /** Class process and load {@link Schema} infomation */ public class SchemaService : Object { - public const string COLUMN_SQL = """ - SELECT column_name, table_name, - case - when domain_name is not null then domain_name - when data_type='character varying' THEN 'varchar('||character_maximum_length||')' - when data_type='numeric' THEN 'numeric('||numeric_precision||','||numeric_scale||')' - else data_type - end as data_type, - is_nullable, column_default - FROM information_schema.columns - WHERE table_schema = $1; - """; - public const string INDEX_SQL = """ - SELECT indexname, tablename, pg_size_pretty(pg_relation_size(indexname::regclass)) as size, indexdef - FROM pg_indexes - WHERE schemaname = $1; - """; - public const string FK_SQL = """ - SELECT con.conname, rel.relname, pg_catalog.pg_get_constraintdef(con.oid, true) as condef - FROM pg_catalog.pg_constraint con - INNER JOIN pg_catalog.pg_class rel - ON rel.oid = con.conrelid - INNER JOIN pg_catalog.pg_namespace nsp - ON nsp.oid = connamespace - WHERE con.contype = 'f' AND nsp.nspname = $1; - """; - public const string TB_SQL = """ - SELECT tablename FROM pg_tables WHERE schemaname=$1; - """; - public const string VIEW_SQL = """ - SELECT table_name FROM INFORMATION_SCHEMA.views WHERE table_schema = $1; - """; - public const string SCHEMA_LIST_SQL = """ SELECT schema_name FROM information_schema.schemata; @@ -49,27 +16,9 @@ namespace Psequel { this.sql_service = service; } - /* Get the schema list as string */ - public async string[] schema_list () throws PsequelError { - - var query = new Query (SCHEMA_LIST_SQL); - var relation = yield sql_service.exec_query (query); - - var _schema_list = new string[relation.rows]; - - for (int i = 0; i < _schema_list.length; i++) { - _schema_list[i] = relation[i][0]; - } - - return _schema_list; - } - /** Get the schema list. - * - * These schemas only contains names.For info about tables, views in schemas, load is with - * {@link load_schema} */ - public async List get_schemas () { + public async List schema_list () { var list = new List (); try { var query = new Query (SCHEMA_LIST_SQL); @@ -85,198 +34,5 @@ namespace Psequel { return list; } - - /** Load table list, view list into the schema. */ - public async void load_schema (Schema schema) throws PsequelError.QUERY_FAIL { - // yield load_tables_and_views (schema); - } - - // private async void load_tables_and_views (Schema schema) { - - // // old table auto clean by GC - // schema.tables = new List (); - // schema.views = new List (); - - // var table_groups = new HashTable (GLib.str_hash, GLib.str_equal); - // var view_groups = new HashTable (GLib.str_hash, GLib.str_equal); - - // var table_names = yield get_tbnames (schema); - - // var view_names = yield get_viewnames (schema); - - // var columns = yield get_columns (schema); - - // var indexes = yield get_indexes (schema); - - // var fks = yield get_fks (schema); - - // debug ("cols: %u indx: %u fks: %u", columns.length (), indexes.length (), fks.length ()); - - // table_names.foreach ((tbname) => { - // var table = new Table (schema) { - // name = tbname, - // }; - // table_groups.insert (tbname, table); - // }); - - // view_names.foreach ((tbname) => { - // var view = new View (schema) { - // name = tbname, - // }; - // view_groups.insert (tbname, view); - // }); - - // columns.foreach ((col) => { - // if (table_groups.contains (col.table)) { - // var table = table_groups.get (col.table); - // table.columns.append (col); - // } - - // if (view_groups.contains (col.table)) { - // var view = view_groups.get (col.table); - // view.columns.append (col); - // } - // }); - - // indexes.foreach ((index) => { - // if (table_groups.contains (index.table)) { - // var table = table_groups.get (index.table); - // table.indexes.append (index); - // } - - // if (view_groups.contains (index.table)) { - // var view = view_groups.get (index.table); - // view.indexes.append (index); - // } - // }); - - // fks.foreach ((fk) => { - // if (table_groups.contains (fk.table)) { - // var table = table_groups.get (fk.table); - // table.foreign_keys.append (fk); - // } - // }); - - // var tables = table_groups.steal_all_values (); - - // for (int i = 0; i < tables.length; i++) { - // schema.tables.append (tables[i]); - // } - - // var views = view_groups.steal_all_values (); - - // for (int i = 0; i < views.length; i++) { - // schema.views.append (views[i]); - // } - // } - - private async List get_tbnames (Schema schema) { - var list = new List (); - - try { - var query = new Query.with_params (TB_SQL, { schema.name }); - var relation = yield sql_service.exec_query_params (query); - - foreach (var row in relation) { - list.append (row[0]); - } - } catch (PsequelError err) { - debug (err.message); - } - - return list; - } - - private async List get_viewnames (Schema schema) { - var list = new List (); - - try { - var query = new Query.with_params (VIEW_SQL, { schema.name }); - var relation = yield sql_service.exec_query_params (query); - - foreach (var row in relation) { - list.append (row[0]); - } - } catch (PsequelError err) { - debug (err.message); - } - - return list; - } - - private async List get_columns (Schema schema) { - - var list = new List (); - - try { - var query = new Query.with_params (COLUMN_SQL, { schema.name }); - var relation = yield sql_service.exec_query_params (query); - - foreach (var row in relation) { - var col = new Column (); - col.schemaname = schema.name; - col.name = row[0]; - col.table = row[1]; - col.column_type = row[2]; - col.nullable = row[3] == "YES" ? true : false; - col.default_val = row[4]; - - list.append (col); - } - } catch (PsequelError err) { - debug (err.message); - } - - return list; - } - - private async List get_indexes (Schema schema) { - - var list = new List (); - - try { - var query = new Query.with_params (INDEX_SQL, { schema.name }); - var relation = yield sql_service.exec_query_params (query); - - foreach (var row in relation) { - var index = new Index (); - index.schemaname = schema.name; - index.name = row[0]; - index.table = row[1]; - index.size = row[2]; - index.indexdef = row[3]; - - list.append (index); - } - } catch (PsequelError err) { - debug (err.message); - } - - return list; - } - - private async List get_fks (Schema schema) { - - var list = new List (); - - try { - var query = new Query.with_params (FK_SQL, { schema.name }); - var relation = yield sql_service.exec_query_params (query); - - foreach (var row in relation) { - var fk = new ForeignKey (); - fk.schemaname = schema.name; - fk.name = row[0]; - fk.table = row[1]; - fk.fk_def = row[2]; - - list.append (fk); - } - } catch (PsequelError err) { - debug (err.message); - } - - return list; - } } } \ No newline at end of file diff --git a/src/ui/schema/QueryEditor.vala b/src/ui/editor/QueryEditor.vala similarity index 95% rename from src/ui/schema/QueryEditor.vala rename to src/ui/editor/QueryEditor.vala index 92d966f..fd15572 100644 --- a/src/ui/schema/QueryEditor.vala +++ b/src/ui/editor/QueryEditor.vala @@ -64,17 +64,13 @@ namespace Psequel { private void on_query_history_exec (Gtk.ListView view, uint pos) { var history_query = (Query)selection_model.get_item (pos); + debug ("History activated, exec: %s", history_query.sql); + query_history_viewmodel.exec_history.begin (history_query); - debug ("%p", history_query); - debug ("%u", selection_model.get_n_items ()); - debug ("%u %i", pos, query_history_viewmodel.query_history.size); - // debug ("History activated, exec: %s", history_query.sql); - // query_history_viewmodel.exec_history.begin (history_query); + var text = history_query.sql ?? ""; + clear_and_insert (buffer, text); - // var text = history_query.sql ?? ""; - // clear_and_insert (buffer, text); - - // popover.hide (); + popover.hide (); } /** Clear and insert insteal of manipulate .text to keep undo possible */ diff --git a/src/ui/editor/SQLCompletionProvider.vala b/src/ui/editor/SQLCompletionProvider.vala new file mode 100644 index 0000000..1cad55c --- /dev/null +++ b/src/ui/editor/SQLCompletionProvider.vala @@ -0,0 +1,124 @@ +namespace Psequel { + public class SQLCompletionProvider : Object, GtkSource.CompletionProvider { + private CompleterService completer; + private Gtk.FilterListModel model; + private Gtk.StringFilter filter; + + private SchemaContext ctx; + + + private SchemaViewModel schema_viewmodel; + public SQLCompletionProvider () { + base (); + this.schema_viewmodel = autowire (); + this.completer = autowire (); + this.ctx = new SchemaContext (); + + // static_candidates = new List (); + // for (int i = 0; i < PGListerals.KEYWORDS.length; i++) { + // static_candidates.append (new Candidate (PGListerals.KEYWORDS[i], "KEYWORD")); + // } + + // for (int i = 0; i < PGListerals.FUNCTIONS.length; i++) { + // static_candidates.append (new Candidate (PGListerals.FUNCTIONS[i], "FUNCTION")); + // } + + // for (int i = 0; i < PGListerals.DATATYPES.length; i++) { + // static_candidates.append (new Candidate (PGListerals.DATATYPES[i], "DATATYPE")); + // } + + // for (int i = 0; i < PGListerals.RESERVED.length; i++) { + // static_candidates.append (new Candidate (PGListerals.RESERVED[i], "RESERVED")); + // } + + // this.notify["query-viewmodel"].connect (() => { + + // dynamic_candidates = new List (); + // query_viewmodel.current_schema.tables.foreach ((table) => { + // dynamic_candidates.append (new Model (table.name, "TABLE")); + // }); + // query_viewmodel.current_schema.views.foreach ((view) => { + // dynamic_candidates.append (new Model (view.name, "VIEW")); + // }); + // }); + + var expression = new Gtk.PropertyExpression (typeof (Candidate), null, "value"); + filter = new Gtk.StringFilter (expression); + filter.match_mode = Gtk.StringFilterMatchMode.PREFIX; + + model = new Gtk.FilterListModel (null, filter); + } + + public void activate (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal) { + var model = (Candidate) proposal; + var buf = context.get_buffer (); + Gtk.TextIter start, end; + + last_word (buf, out start, out end); + + buf.delete_range (start, end); + buf.insert_at_cursor (model.value, model.value.length); + // buf.insert (ref iter, model.value, model.value.length); + } + + public void display (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal, GtkSource.CompletionCell cell) { + + var candidate = (Candidate) proposal; + switch (cell.column) { + // name + case GtkSource.CompletionColumn.TYPED_TEXT: + cell.text = candidate.value; + break; + + // group + case GtkSource.CompletionColumn.AFTER: + cell.text = candidate.group; + break; + // case GtkSource.CompletionColumn.DETAILS: + // cell.text = "DETAILS"; + // break; + // case GtkSource.CompletionColumn.COMMENT: + // cell.text = "COMMENT"; + // break; + // case GtkSource.CompletionColumn.ICON: + // cell.text = "ICON"; + // break; + // case GtkSource.CompletionColumn.BEFORE: + // cell.text = candidate.value; + // break; + default: break; + } + } + + public async GLib.ListModel populate_async (GtkSource.CompletionContext context, GLib.Cancellable? cancellable) { + + var word = last_word (context.get_buffer ()); + var candidates = new ObservableList (); + + candidates.append_all (completer.get_suggestions (this.ctx, word)); + model.model = candidates; + + return model; + } + + public void refilter (GtkSource.CompletionContext context, GLib.ListModel _model) { + var word = last_word (context.get_buffer ()); + var candidates = new ObservableList (); + candidates.append_all (completer.get_suggestions (this.ctx, word)); + model.model = candidates; + } + + private string last_word (GtkSource.Buffer buf, out Gtk.TextIter start = null, out Gtk.TextIter end = null) { + Gtk.TextIter iter; + buf.get_iter_at_offset (out iter, buf.cursor_position); + + start = iter; + end = iter; + + start.backward_word_start (); + end.forward_word_end (); + + return buf.get_text (start, end, false); + } + } +} \ No newline at end of file diff --git a/src/viewmodels/SchemaViewModel.vala b/src/viewmodels/SchemaViewModel.vala index 2f17668..ef1fa93 100644 --- a/src/viewmodels/SchemaViewModel.vala +++ b/src/viewmodels/SchemaViewModel.vala @@ -30,7 +30,10 @@ namespace Psequel { public void update (Event event) { if (event.type == Event.ACTIVE_CONNECTION) { - database_connected.begin (); + // delay for ui to play animation + Timeout.add_once (300, () => { + database_connected.begin (); + }); } } @@ -44,6 +47,7 @@ namespace Psequel { private async void database_connected () throws PsequelError { // auto load schema list. yield list_schemas (); + yield select_schema (schemas.find (s => s.name == DEFAULT)); } @@ -57,7 +61,7 @@ namespace Psequel { /** List schema from database. */ private async void list_schemas () throws PsequelError { - var unload_schemas = yield schema_service.get_schemas (); + var unload_schemas = yield schema_service.schema_list (); schemas.append_all (unload_schemas); }