From 400cbc2cc74ccec03bee36ca9a009fe3a9fccb59 Mon Sep 17 00:00:00 2001 From: lsv Date: Tue, 11 Nov 2025 10:06:55 +0500 Subject: [PATCH] Add autocomplite dlgFunction. Fix multibyte char support. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлено использование автоподстановки в окно редактирования процедур и функции. Добавлена поддержка UTF-8. Добавлена поддержка unicode для идентификаторов. Добавлено сохранение выбранных опций в диалоге Выравнивания. --- ctl/ctlSQLBox.cpp | 46 ++++++++++++++++++---- dlg/dlgFunction.cpp | 2 + include/ctl/ctlSQLBox.h | 1 + include/db/pgConn.h | 5 +++ utils/tabcomplete.c | 86 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 130 insertions(+), 10 deletions(-) diff --git a/ctl/ctlSQLBox.cpp b/ctl/ctlSQLBox.cpp index fa42891..71ed9d5 100644 --- a/ctl/ctlSQLBox.cpp +++ b/ctl/ctlSQLBox.cpp @@ -89,7 +89,7 @@ ctlSQLBox::ctlSQLBox(wxWindow *parent, wxWindowID id, const wxPoint &pos, const m_dlgFindReplace = 0; m_dlgTransformText = 0; m_database = NULL; - + m_autocompDisabled = false; process = 0; processID = 0; @@ -1344,10 +1344,11 @@ wxString ctlSQLBox::ExternalFormat(int typecmd) break; } wxArrayString choiceCmpOpts; - wxArrayInt choiceSelectOpts; + choiceCmpOpts.Add(_("All line (use all EOL)")); choiceCmpOpts.Add(_("First line pattern (ignore all but the first EOL)")); choiceCmpOpts.Add(_("Try looking for patterns above")); + choiceCmpOpts.Add(_("Compact view")); choiceCmpOpts.Add(_("Remove multi spaces")); wxMultiChoiceDialog dialog(this, _("A multi-choice convenience dialog"), @@ -1362,7 +1363,8 @@ wxString ctlSQLBox::ExternalFormat(int typecmd) if (choiceSelectOpts[n] == 0) cfg |= AlignWrap::ALL_LINES; if (choiceSelectOpts[n] == 1 ) cfg |= AlignWrap::FIRST_LINE ; if (choiceSelectOpts[n] == 2) cfg |= AlignWrap::FIND_UP_LONG_LINE; - if (choiceSelectOpts[n] == 3) cfg |= AlignWrap::ONLY_SINGLE_SPACE; + if (choiceSelectOpts[n] == 3) cfg |= AlignWrap::COMPACT_VIEW; + if (choiceSelectOpts[n] == 4) cfg |= AlignWrap::ONLY_SINGLE_SPACE; } if (CHKCFGPARAM(cfg, AlignWrap::ALL_LINES) && CHKCFGPARAM(cfg, AlignWrap::FIRST_LINE)) cfg -= AlignWrap::FIRST_LINE; @@ -1866,9 +1868,31 @@ void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev) if (m_autocompDisabled) return; int pos = GetCurrentPos(); - wxString what = GetCurLine().Left(pos - PositionFromLine(GetCurrentLine()));; + //wxString what = GetCurLine().Left(pos - PositionFromLine(GetCurrentLine())); + wxString what = GetTextRange(PositionFromLine(GetCurrentLine()),pos); int spaceidx = what.Find(' ', true); - + int spacecharidx=spaceidx; + int poshome=PositionFromLine(GetCurrentLine()); + int posspc=PositionRelative(poshome,spaceidx); + if (spaceidx != -1) { + + while (poshome< posspc) { + int ch = GetCharAt(posspc); + int st = GetStyleAt(posspc) & 0x1F; + if (st == wxSTC_SQL_STRING || st == wxSTC_SQL_CHARACTER) + { + posspc--; + } else if (ch=='"' || ch=='.') { + posspc--; + } else if (ch==' ') { + break; + } + + } + wxString lastexp=GetTextRange(PositionFromLine(GetCurrentLine()),posspc); + spacecharidx=lastexp.Length(); + spaceidx=posspc-poshome; + } char *tab_ret; if (spaceidx == -1) tab_ret = tab_complete(what.mb_str(wxConvUTF8), 0, what.Len() + 1, m_database); @@ -2072,7 +2096,7 @@ void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev) if (found) { wxString flt=""; if (!fld.IsEmpty()) flt = " and a.attname ~ " + qtConnString(fld); - wxString sql=wxT("select string_agg(a.attname,E'\t') from pg_attribute a where a.attrelid = (select oid from pg_class p where relname=") +qtConnString(table) + wxString sql=wxT("select string_agg(a.attname,E'\t') from pg_attribute a where a.attrelid in (select oid from pg_class p where relname=") +qtConnString(table) +wxT(") and a.attisdropped IS FALSE and a.attnum>=0 ")+flt +wxT(" order by 1"); //pgSet *res = m_database->ExecuteSet(sql); @@ -2134,7 +2158,8 @@ void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev) if (spaceidx == -1) AutoCompShow(what.Len(), wxRet); else - AutoCompShow(what.Len() - spaceidx - 1, wxRet); + //AutoCompShow(what.Len() - spacecharidx - 1, wxRet); + AutoCompShow(pos - posspc - 1, wxRet); // Now switch back #ifdef __WXMAC__ @@ -2201,7 +2226,12 @@ char *pg_query_to_single_ordered_string(char *query, void *dbptr) return strdup(ret.mb_str(wxConvUTF8)); } - +extern "C" +int get_id_encoding(void *dbptr) +{ + pgConn *db = (pgConn *)dbptr; + return db->Get_client_encoding_id(); +} // Find some text in the document. CharacterRange ctlSQLBox::RegexFindText(int minPos, int maxPos, const wxString &text) diff --git a/dlg/dlgFunction.cpp b/dlg/dlgFunction.cpp index b9617db..67c19bc 100644 --- a/dlg/dlgFunction.cpp +++ b/dlg/dlgFunction.cpp @@ -154,6 +154,8 @@ dlgFunction::dlgFunction(pgaFactory *f, frmMain *frame, pgFunction *node, pgSche txtSqlBox->SetMarginType(1, wxSTC_MARGIN_NUMBER); txtSqlBox->SetMarginWidth(1, ConvertDialogToPixels(wxPoint(16, 0)).x); } + if (function && function->GetDatabase()) + txtSqlBox->SetDatabase(function->GetDatabase()->GetConnection()); btnAdd->Disable(); btnRemove->Disable(); btnChange->Disable(); diff --git a/include/ctl/ctlSQLBox.h b/include/ctl/ctlSQLBox.h index 317c677..680c295 100644 --- a/include/ctl/ctlSQLBox.h +++ b/include/ctl/ctlSQLBox.h @@ -135,6 +135,7 @@ private: dlgFindReplace *m_dlgFindReplace; dlgTransformText *m_dlgTransformText; pgConn *m_database; + wxArrayInt choiceSelectOpts; bool m_autoIndent, m_autocompDisabled, m_hint_mode; struct InsensitiveCompare { bool operator() (const wxString& a, const wxString& b) const { diff --git a/include/db/pgConn.h b/include/db/pgConn.h index d7d68c8..cff2f6f 100644 --- a/include/db/pgConn.h +++ b/include/db/pgConn.h @@ -253,6 +253,11 @@ public: { return conn; } + int Get_client_encoding_id() + { + return PQclientEncoding(conn); + } + void Notice(const char *msg); pgNotification *GetNotification(); int GetTxStatus(); diff --git a/utils/tabcomplete.c b/utils/tabcomplete.c index 26ce491..77a93cb 100644 --- a/utils/tabcomplete.c +++ b/utils/tabcomplete.c @@ -26,7 +26,7 @@ * Callbacks to the C++ world */ char *pg_query_to_single_ordered_string(char *query, void *dbptr); - +int get_id_encoding(void *dbptr); /* * Global vars for readline emulation @@ -170,6 +170,7 @@ static char *complete_from_schema_query(const char *text, const void* query, con static char *complete_create_command(char *text); static char *complete_filename(); + /* * Include the main tab completion functionality from psql */ @@ -226,16 +227,97 @@ static char *complete_from_const(const char *text, const char *string) { return strdup(string); } +static int lower_identifier(const char *ident, char *out,void *dbptr) +{ + size_t buflen = strlen(ident) + 1; + + char *sname; + char *oname; + char *optr; + char *tmp; + int inquotes=0; + int isneedlower=0; + int schemaquoted,objectquoted; + int encoding=6; // pg_enc.PG_UTF8 + int enc_is_single_byte = 0; + int utf8len=0; + /* Initialize, making a certainly-large-enough output buffer */ + schemaquoted = objectquoted = 0; + /* Scan */ + inquotes = 0; + optr=out; + while (*ident) + { + unsigned char ch = (unsigned char) *ident++; + utf8len++; + if (ch == '"') + { + if (inquotes && *ident == '"') + { + /* two quote marks within a quoted identifier = emit quote */ + *optr++ = '"'; + ident++; + utf8len++; + } + else + { + inquotes = !inquotes; + objectquoted = 1; + *optr++ = '"'; + } + } + else if (!enc_is_single_byte && ch>127) + { + /* + * Transfer multibyte characters without further processing. They + * wouldn't be affected by our downcasing rule anyway, and this + * avoids possibly doing the wrong thing in unsafe client + * encodings. + */ + int chlen = PQmblenBounded(ident - 1, encoding); + + *optr++ = (char) ch; + while (--chlen > 0) + *optr++ = *ident++; + } + else + { + if (!inquotes) + { + /* + * This downcasing transformation should match the backend's + * downcase_identifier() as best we can. We do not know the + * backend's locale, though, so it's necessarily approximate. + * We assume that psql is operating in the same locale and + * encoding as the backend. + */ + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + else if (enc_is_single_byte && ch>127 && isupper(ch)) + ch = tolower(ch); + } + *optr++ = (char) ch; + } + } + *optr = '\0'; + return utf8len; +} static char* _complete_from_query(const char* text, const char* query, const SchemaQuery* squery, const char* addon, void* dbptr) { int string_length = strlen(text); char* e_text; + char* e_text_ident; char* complete_query = NULL; char* t; + e_text_ident = malloc(string_length * 2 + 1); + PQescapeString(e_text_ident,text, strlen(text)); + string_length=strlen(e_text_ident); e_text = malloc(string_length * 2 + 1); - PQescapeString(e_text, text, string_length); + int utf8len=lower_identifier(e_text_ident,e_text,dbptr); + string_length=utf8len; + free(e_text_ident); if (query != NULL) {