diff --git a/ctl/ctlSQLBox.cpp b/ctl/ctlSQLBox.cpp index 67ae549..557abd5 100644 --- a/ctl/ctlSQLBox.cpp +++ b/ctl/ctlSQLBox.cpp @@ -24,7 +24,7 @@ #include "utils/sysProcess.h" #include #include - +#include "utils/align/AlignWrap.h" wxString ctlSQLBox::sqlKeywords; static const wxString s_leftBrace(_T("([{")); static const wxString s_rightBrace(_T(")]}")); @@ -952,6 +952,50 @@ wxString ctlSQLBox::ExternalFormat(int typecmd) } if (formatCmd.IsEmpty()) { + if (typecmd == 1) { + //internal align + AlignWrap a; + wxString lineEnd; + switch (GetEOLMode()) + { + case wxSTC_EOL_LF: + lineEnd = wxT("\n"); + break; + case wxSTC_EOL_CRLF: + lineEnd = wxT("\r\n"); + break; + case wxSTC_EOL_CR: + lineEnd = wxT("\r"); + 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"); + wxMultiChoiceDialog dialog(this, + wxT("A multi-choice convenience dialog"), + wxT("Please select several align options"), + choiceCmpOpts); + dialog.SetSelections(choiceSelectOpts); + int cfg = 0; + if (dialog.ShowModal() == wxID_OK) { + choiceSelectOpts = dialog.GetSelections(); + + for (size_t n = 0; n < choiceSelectOpts.GetCount(); n++) { + 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 (CHKCFGPARAM(cfg, AlignWrap::ALL_LINES) && CHKCFGPARAM(cfg, AlignWrap::FIRST_LINE)) cfg -= AlignWrap::FIRST_LINE; + } + else return _("Cancel"); + + + processOutput=a.build(processInput, cfg, lineEnd); + goto theend; + } return _("You need to setup a "+msgword+"ing command"); } @@ -1007,7 +1051,7 @@ wxString ctlSQLBox::ExternalFormat(int typecmd) { return _("" + msgword + "ing command error: Output is empty."); } - +theend: if (isSelected) ReplaceSelection(processOutput); else diff --git a/include/utils/align/AlignWrap.h b/include/utils/align/AlignWrap.h new file mode 100644 index 0000000..25dd43c --- /dev/null +++ b/include/utils/align/AlignWrap.h @@ -0,0 +1,39 @@ +#pragma once +#include "Item.h" + +class AlignWrap { +public: + enum cfg + { + // Выравнивание списка строк можно использовать вместе с FIND_UP_LONG_LINE + // например insert команды с одинаковым перечнем элементов + ALL_LINES = 1, + // выравнивание по длинне первой строки + // все переводы строк начиная со второй строки игнорируются + // удобно для выравнивания списков IN + FIRST_LINE= 2, + // вспомогательный флаг применяется если встречаются случайные короткие строки + // и при помощи этого флага ищутся более длинные строки обработанные ранее + FIND_UP_LONG_LINE=4 + }; + AlignWrap() {} +#define CHKCFGPARAM(val,par) ((val & par)==par) + + wxString build(wxString & strsrc, int config,wxString linesep); + void Resize(int idx, int newSize); + wxString range_print(int s, int e); +private: + + int range_size(int s, int e); + int range_size(int s); + Item parseItem(int &pos, bool &breakline); + int chkspace(int &pos, bool& br); + int find(int s, int e, Item& k); + // + int parserows = 0; + + std::vector list; + + wxString str, lnsep; + int cfg = 0; +}; diff --git a/include/utils/align/Item.h b/include/utils/align/Item.h new file mode 100644 index 0000000..0527da0 --- /dev/null +++ b/include/utils/align/Item.h @@ -0,0 +1,48 @@ +#pragma once +#include +class Item { +public: + enum align + { + LEFT = 0, + RIGHT + }; + enum type + { + SPACE = 0, + NUM, + WORD, + LITERAL, + ANCHOR, + COMMENT + }; + + Item(const wxString &str, int type_item, int align_type=LEFT) { + it = str; + type = type_item; + align = align_type; + } + bool operator==(const Item& rh); + bool operator!=(const Item& rh); + wxString print(); + wxString println(); + const wxString getValue() { return it; } + const wxString getComment() { return comment; } + const int getMaxSize(); + const int getParent() { return up_item; } + void setMaxSize(int newSize); + const void add_space(int align, int count_space) { if (align == Item::align::LEFT) rs += count_space; else ls+= count_space;} + const void setParent(int idxPar) { up_item = idxPar; }; + const void setComment(wxString c) { comment = c; }; + int rs = 0; + int ls = 0; + int type = 0; + int up_item = -1; + bool br = false; +private: + int align = LEFT; + int maxlen = 0; + wxString it; + wxString comment; + +}; diff --git a/pgAdmin3.vcxproj b/pgAdmin3.vcxproj index f09d33d..2517b40 100644 --- a/pgAdmin3.vcxproj +++ b/pgAdmin3.vcxproj @@ -1012,6 +1012,8 @@ + + NotUsing @@ -1570,6 +1572,8 @@ + + diff --git a/pgAdmin3.vcxproj.filters b/pgAdmin3.vcxproj.filters index 91f222f..ceaa67a 100644 --- a/pgAdmin3.vcxproj.filters +++ b/pgAdmin3.vcxproj.filters @@ -1668,6 +1668,12 @@ ctl + + utils + + + utils + @@ -3587,6 +3593,12 @@ include\ctl + + include\utils + + + include\utils + diff --git a/utils/align/AlignWrap.cpp b/utils/align/AlignWrap.cpp new file mode 100644 index 0000000..82ba6d2 --- /dev/null +++ b/utils/align/AlignWrap.cpp @@ -0,0 +1,368 @@ +#include "pgAdmin3.h" +#include "utils/align/AlignWrap.h" +#include + + +wxString AlignWrap::build(wxString& strsrc, int config,wxString linesep) +{ + str = strsrc; + cfg = config; + lnsep = linesep; + bool br = false; + int p = 0; + list.clear(); + std::vector nline; // позиции в list начала строк + int len = strsrc.Length(); + nline.push_back(0); + int nrow = -1; + // parse items + while (p < len) { + Item i = parseItem(p, br); + if (i.type == Item::type::SPACE) break; + if (i.type == Item::type::COMMENT) { + if (list.size() == 0) return strsrc; + Item k = list[list.size() - 1]; + wxString cc = i.getValue(); + cc = cc.Trim(true); + + if (k.br) { + cc = lnsep + cc; + //if (CHKCFGPARAM(cfg, ALL_LINES)) + br = false; // чтобы повторно не добавлять новую строку в nline + } + k.setComment(k.getComment()+cc); + k.br = i.br; + list.erase(list.end()-1); + i = k; + + }; + list.push_back(i); +#ifdef _DEBUG + //std::cerr << i.println(); +#endif + if (br) { + nline.push_back(list.size()); // следующий элемент будет с новой строки + nrow++; + } + br = false; + } + if (list.size() == 0) return strsrc; + nline.push_back(list.size() - 1); + for (size_t l = 1; l < nline.size() - 1; l++) + { + int index_u = nline[l - 1L]; + int index_c = nline[l]; + int CountItemU = index_c - index_u; + int maxIdxU = index_c; + int maxIdxC = nline[l + 1L]; + + size_t ll = l; + if (CHKCFGPARAM(cfg, FIND_UP_LONG_LINE)) { + while ((ll > 0) && ((((int)nline[ll] - (int)nline[ll - 1L]) - (maxIdxC - maxIdxU)) < 0)) { + // верхняя строка короче чем текущяя поднимемся вверх для поиска более длинной + ll--; + } + if (ll == 0) ll = l; + } + index_u = nline[ll - 1L]; + maxIdxU= nline[ll]; + int size_u = 0; + int size_c = 0; + // : 3456,(wwww) + // , : 7,(a) + while (index_u < maxIdxU && index_c < maxIdxC) { + int idxU = find(index_u, maxIdxU, list[index_c]); + // проверим обратную ситуацию верхний найдём в текущей строке + int idxC = find(index_c, maxIdxC, list[index_u]); + if (idxU < 0) idxU = index_u; + if (idxC < 0) idxC = index_c; + int dc = (idxC - index_c); + int du = (idxU - index_u); + size_u = range_size(index_u, idxU); + size_c = range_size(index_c, idxC); + if (du==0 && dc==0 ) { + // элементы совпадают и их можно выровнять + // определим в какой строке это будем делать + list[index_c].setParent(index_u); + } + else { + // 4021196,'fffff',( + // (4155,'aaaa' + if (size_u > size_c) { // фиксируем верхний + // будем нижний подгонять под верхний + size_u = range_size(index_u); + idxU = index_u; + } else + if (size_u < size_c) { + size_c = range_size(index_c); + idxC = index_c; + } + list[idxC].setParent(idxU); + } + if (size_u < size_c) { + // вырхний короче, его дополняем + list[index_u].add_space(Item::align::LEFT, size_c - size_u); + Resize(index_u, list[index_u].getMaxSize()); + idxU = idxU + 1; + idxC++; + + } + if (size_u > size_c) { + // + list[index_c].add_space(Item::align::LEFT, size_u - size_c); + Resize(index_c, list[index_c].getMaxSize()); + idxC = idxC + 1; + idxU++; + } + if (size_u == size_c) { + idxC++; + idxU++; + } + index_c = idxC; + index_u = idxU; + } + // + if (CHKCFGPARAM(cfg, FIRST_LINE)) { + if (index_u >= maxIdxU) { + list[(size_t)index_c - 1].br = true; + size_t id = (nline.size() - nrow - 1); + + if (nline[id] != index_c) + nline.insert(nline.end() - nrow - 1, index_c); + else nrow--; + } + else + { + // из за коментарией -- мы внесли перевод в неожидааных местах и их нужно учесть когда до них дойдём + if (nrow == 0) break; + nrow--; + cfg = cfg | FIND_UP_LONG_LINE; + } + } + } + wxString a; + for (int j = 0; j < nline.size() - 1; j++) { + int d = 1; + if (j == nline.size() - 2) d = 0; + a+=range_print(nline[j], nline[j + 1] - d); + } + //range_print(nline[0], nline[1L]-1); + //range_print(nline[l], nline[l + 1L]-1); + return a; +} + +void AlignWrap::Resize(int idx,int newSize) { + int pr = idx; + while (pr>=0) { + Item &i=list[pr]; + int oldsz = i.getMaxSize(); + pr = i.getParent(); + i.setMaxSize(newSize); + //if (pr>=0) Resize(pr,newSize); + } +} + + +int AlignWrap::range_size(int s) { + int se = 0; + se = list[s].getMaxSize(); + return se; +} +int AlignWrap::range_size(int s, int e) { + int se = 0; + for (int j = s; j <= e; j++) { + se += list[j].getMaxSize(); + } + return se; +} +wxString AlignWrap::range_print(int s, int e) { + wxString ss = ""; + for (int j = s; j <= e; j++) { + ss = ss+list[j].print(); + + } + ss += lnsep; + //std::cout << ss; + return ss; +} + +int AlignWrap::find(int s,int e, Item &k) { + + std::vector::iterator it; + if (e >= list.size()) e = list.size() - 1; + for (int j = s; j <= e; j++) { + if (list[j] == k) { + return j; + } + } + return -1; +} +Item AlignWrap::parseItem(int& pos, bool& breakline) { + + wxChar c; + wxChar c2; + int p = pos; + int len = str.Length(); + int l = 0; + int sp = 0; + int spl = 0; + int spbe = 0; + int type; + int comment = 0; + while (true) { + spl = chkspace(p, breakline); + if (breakline) { + // empty line ignore + breakline = false; + spl = 0; + continue; + } + pos = p; + break; + } + if (p >= len) { + Item i("", 0); + return i; + } + c = str[p]; + if (c == '\'' || c == '"') { + p++; + + while (p < len) + { + c2 = str[p]; + if (c2 == c) { + if (p + 1 < len) + if (str[p+1] == c) { p = p + 2; continue; } + p++; + break; + } + p++; + } + l = p - pos; + type = Item::type::LITERAL; + goto theend; + + } + if ((p + 1) < len) c2 = str[p + 1]; + + if (wxIsalpha(c)) { + // WORD + p++; + while (p < len) + { + c = str[p]; + if (!(wxIsalnum(c) || c == '_' || c == '$'|| c==':'|| c=='.')) break; + p++; + } + l = p - pos; + type = Item::type::WORD; + goto theend; + + } + if ((c == '-' || wxIsdigit(c)) &&(c2!='-')) { + p++; + while (p < len) + { + c = str[p]; + if (!(wxIsdigit(c) || c == '.' || c == 'e' || c == 'E')) break; + p++; + } + l = p - pos; + type = Item::type::NUM; + goto theend; + + } + type = Item::type::ANCHOR; + if (p + 1 < len) { + p++; + c2 = str[p]; + p++; + l = p - pos; + // 2-х символьные операторы + if (c == '<' && c2 == '=') goto theend; + if (c == '>' && c2 == '=') goto theend; + if (c == '<' && c2 == '>') goto theend; + if (c == '-' && c2 == '-') { + int cntNL = 0; + while (p < len) + { + if (str[p] == '\n' || str[p] == '\r') { + cntNL++; + } + else { + if (cntNL > 0) break; + } + p++; + + } + l = p - pos; + comment = 1; + type = Item::type::COMMENT; + goto theend; + } + if (c == '/' && c2 == '*') { + while (p < (len - 1)) + { + if (str[p] == '*' || str[p + 1] == '/') + break; + p++; + } + p=p+2; + l = p - pos; + type = Item::type::COMMENT; + goto theend; + } + p = p - 2; + } + p++; + l = p - pos; + + goto theend; + + +theend: + wxString v = str.substr(pos, l); + sp = chkspace(p, breakline); + if (comment == 1) breakline = true; + Item i(v,type); + i.rs = sp; + //i.ls = spl; + i.br = breakline; + pos = p; + return i; +} +int AlignWrap::chkspace(int &pos, bool& br) { + wxChar c,prev='\0'; + int nl = parserows; + int l = 0; + bool endline = false; + while (pos < str.Length()) { + c = str[pos++]; + if (c == '\n' || c == '\r') { + if (c=='\r' && str[pos] == '\n') { + // windows + endline = true; + pos++; + } + parserows++; + if (CHKCFGPARAM(cfg,ALL_LINES)) { + br = true; + break; + } + if ((CHKCFGPARAM(cfg,FIRST_LINE)) && parserows == 1) + { + // first row + br = true; + break; + } + continue; + } + + if (c == ' ' || c == '\t') { l++; continue; } + pos--; + break; + } + if (nl < parserows) l++; + return l; +} \ No newline at end of file diff --git a/utils/align/Item.cpp b/utils/align/Item.cpp new file mode 100644 index 0000000..ca2841d --- /dev/null +++ b/utils/align/Item.cpp @@ -0,0 +1,63 @@ +#include "pgAdmin3.h" +#include "utils/align/Item.h" +bool Item::operator==(const Item& rh) +{ + if (type == rh.type && type == ANCHOR) { + if (it == rh.it) return true; + return false; + } + // null ( NUM or LITERAL) + if (it.IsSameAs("null", false) && true) { + if (rh.type == LITERAL || rh.type == NUM || rh.type == WORD) + return true; + return false; + } + //NUM or LITERAL + if (type == NUM || type == LITERAL ) { + if (rh.it.IsSameAs("null", false) && true) + return true; + if (rh.type == LITERAL || rh.type == NUM ) return true; + return false; + } + // + if (type == rh.type) return true; + + return false; +} +bool Item::operator!=(const Item& rh) { + return !this->operator==(rh); +} +wxString Item::println() { + wxString s = wxString::Format("Type %d val:\"%s\"", type, it); + if (ls > 0) s += wxString::Format(" L:\"%d\"", ls); + if (rs > 0) s += wxString::Format(" R:\"%d\"", rs); + if (br) s += wxString::Format(" BR"); + s += wxString::Format("\n"); + return s; +} +wxString Item::print() { + + wxString s = wxString::Format("%s", it); + wxString r(' ', rs), l(' ', ls); + + s = l + s + r + wxString::Format("%s", comment); + std::cout << s; + return s; +} +const int Item::getMaxSize() { + int tmp = rs + ls + it.Length(); + if (!br) tmp += comment.Length(); + return tmp; +} +void Item::setMaxSize(int newSize) { + ls = 0; + int tmp = newSize - it.Length() - comment.Length(); + if (tmp >= 0) { + rs = tmp; + } + else { + //this->println(); + return; + } +} +