diff --git a/src/Bridge.cpp b/src/Bridge.cpp index 7b55a2b798..3291642d1b 100644 --- a/src/Bridge.cpp +++ b/src/Bridge.cpp @@ -1014,3 +1014,186 @@ extern "C" void EditCopyAsRTF(HWND hwnd) { } catch (...) { } } + +namespace { + +enum { + SpaceOption_None = 0, + SpaceOption_IndentAfter = 1, + SpaceOption_SpaceBefore = 2, + SpaceOption_SpaceAfter = 4, + SpaceOption_NewLineBefore = 8, + SpaceOption_NewLineAfter = 16, +}; + +bool AddStyleSeparator(int ch, int chPrev, int style, LPCEDITLEXER pLex) noexcept { + // a++ + ++b, a-- - --b + if ((ch == '+' || ch == '-') && ch == chPrev) { + return true; + } + // var a; property: 1 #111 + if (IsAlphaNumeric(chPrev) || chPrev == '_') { + if (IsAlphaNumeric(ch) || ch == '_' || ch == '$' || ch == '#') { + return true; + } + if ((ch == '.' || ch == '-') && style != pLex->operatorStyle && style != pLex->operatorStyle2) { + return true; + } + } + return false; +} + +std::string CodePretty(LPCEDITLEXER pLex, const char *styledText, size_t textLength) { + std::string output; + uint32_t blockLevel = 0; + int chPrev = 0; + char eol[4]{}; + unsigned eolWidth = SciCall_GetEOLMode(); + if (eolWidth == SC_EOL_CR) { + eolWidth = 1; + eol[0] = '\r'; + } else if (eolWidth == SC_EOL_LF) { + eolWidth = 1; + eol[0] = '\n'; + } else { + eolWidth = 2; + eol[0] = '\r'; + eol[1] = '\n'; + } + + int stylePrev = static_cast(styledText[0]); + const char * const textBuffer = styledText + textLength; + for (size_t offset = 0; offset < textLength; offset++) { + const uint8_t style = styledText[offset]; + if (style == 0) { + stylePrev = style; + continue; + } + + int spaceOption = SpaceOption_None; + const uint8_t ch = textBuffer[offset]; + if (style != stylePrev && style > pLex->commentStyleMarker) { + if (AddStyleSeparator(ch, chPrev, style, pLex)) { + spaceOption = SpaceOption_SpaceBefore; + } + } + if (style == pLex->operatorStyle || style == pLex->operatorStyle2) { + if (ch == ':') { + spaceOption |= SpaceOption_SpaceAfter; + } else if (ch == ',') { + if (pLex->iLexer == SCLEX_JSON) { + spaceOption |= SpaceOption_NewLineAfter; + } else { + spaceOption |= SpaceOption_SpaceAfter; + } + } else if (ch == ';') { + spaceOption |= SpaceOption_NewLineAfter; + } else if (ch == '{' || (ch == '[' && pLex->iLexer == SCLEX_JSON)) { + spaceOption |= SpaceOption_NewLineAfter | SpaceOption_IndentAfter; + if (pLex->iLexer == SCLEX_CSS) { + spaceOption |= SpaceOption_SpaceBefore; + } + } else if (ch == '}' || (ch == ']' && pLex->iLexer == SCLEX_JSON)) { + spaceOption |= SpaceOption_NewLineBefore | SpaceOption_NewLineAfter; + if (blockLevel > 0) { + --blockLevel; + } + } + if (chPrev == '\n' && (ch == ',' || ch == ')' || ch == ';')) { + chPrev = '\0'; + output.erase(output.end() - eolWidth, output.end()); + } + } + if (chPrev > ' ') { + if (spaceOption & SpaceOption_NewLineBefore) { + chPrev = '\n'; + output += std::string_view{eol, eolWidth}; + } else if (spaceOption & SpaceOption_SpaceBefore) { + output += ' '; + } + } + if (chPrev == '\n' && blockLevel > 0) { + uint32_t count = blockLevel; + char indent = '\t'; + if (fvCurFile.bTabsAsSpaces) { + indent = ' '; + count *= fvCurFile.iTabWidth; + } + while (count != 0) { + --count; + output += indent; + } + } + if (ch == '\r' || ch == '\n') { + spaceOption |= SpaceOption_NewLineAfter; + if (ch == '\r' && textBuffer[offset + 1] == '\n') { + offset += 1; + } + } else { + chPrev = ch; + output += static_cast(ch); + } + if (spaceOption & SpaceOption_NewLineAfter) { + blockLevel += spaceOption & SpaceOption_IndentAfter; + chPrev = '\n'; + output += std::string_view{eol, eolWidth}; + } else if (spaceOption & SpaceOption_SpaceAfter) { + chPrev = ' '; + output += ' '; + } + stylePrev = style; + } + return output; +} + +} + +extern "C" void EditFormatCode(int menu) { + LPCEDITLEXER pLex = pLexCurrent; + if (pLex->iLexer != SCLEX_JSON && pLex->iLexer != SCLEX_CSS && pLex->iLexer != SCLEX_JAVASCRIPT) { + return; + } + const Sci_Position startPos = SciCall_GetSelectionStart(); + const Sci_Position endPos = SciCall_GetSelectionEnd(); + if (startPos == endPos) { + return; + } + + SciCall_EnsureStyledTo(endPos); + try { + const std::unique_ptr styledText = make_unique_for_overwrite(2*(endPos - startPos) + 1); + const Sci_TextRangeFull tr { { startPos, endPos }, styledText.get() }; + const size_t textLength = SciCall_GetStyledTextFull(&tr); + + if (menu == IDM_EDIT_CODE_COMPRESS) { + size_t index = 0; + int chPrev = 0; + int stylePrev = static_cast(styledText[0]); + const char * const textBuffer = styledText.get() + textLength; + for (size_t offset = 0; offset < textLength; offset++) { + const uint8_t style = styledText[offset]; + if (style > pLex->commentStyleMarker) { + const uint8_t ch = textBuffer[offset]; + if (style != stylePrev) { + if (AddStyleSeparator(ch, chPrev, style, pLex)) { + styledText[index++] = ' '; + } + } + chPrev = ch; + styledText[index++] = static_cast(ch); + } + stylePrev = style; + } + styledText[index] = '\0'; + if (index < textLength) { + EditReplaceMainSelection(index, styledText.get()); + } + } else { + const std::string output = CodePretty(pLex, styledText.get(), textLength); + if (output.length() != textLength) { + EditReplaceMainSelection(output.length(), output.c_str()); + } + } + } catch (...) { + } +} diff --git a/src/Edit.h b/src/Edit.h index 2dd7359e7b..8cf4d1ec60 100644 --- a/src/Edit.h +++ b/src/Edit.h @@ -133,6 +133,7 @@ void EditDetectEOLMode(LPCSTR lpData, DWORD cbData, struct EditFileIOStatus *st bool EditLoadFile(LPWSTR pszFile, struct EditFileIOStatus *status); bool EditSaveFile(HWND hwnd, LPCWSTR pszFile, int saveFlag, struct EditFileIOStatus *status); +void EditReplaceMainSelection(Sci_Position cchText, LPCSTR pszText); void EditInvertCase(void); void EditMapTextCase(int menu); void EditSentenceCase(void); @@ -223,6 +224,7 @@ extern "C" { bool EditPrint(HWND hwnd, LPCWSTR pszDocTitle, BOOL bDefault); void EditPrintSetup(HWND hwnd); void EditCopyAsRTF(HWND hwnd); +void EditFormatCode(int menu); #ifdef __cplusplus } diff --git a/src/Notepad2.c b/src/Notepad2.c index 889cf9fe6d..f3367f8b37 100644 --- a/src/Notepad2.c +++ b/src/Notepad2.c @@ -2526,6 +2526,8 @@ void MsgInitMenu(HWND hwnd, WPARAM wParam, LPARAM lParam) { EnableCmd(hmenu, IDM_EDIT_URLENCODE, i); EnableCmd(hmenu, IDM_EDIT_URLDECODE, i); + EnableCmd(hmenu, IDM_EDIT_CODE_COMPRESS, i); + EnableCmd(hmenu, IDM_EDIT_CODE_PRETTY, i); EnableCmd(hmenu, IDM_EDIT_XHTML_ESCAPE_CHAR, i); EnableCmd(hmenu, IDM_EDIT_XHTML_UNESCAPE_CHAR, i); @@ -2536,6 +2538,8 @@ void MsgInitMenu(HWND hwnd, WPARAM wParam, LPARAM lParam) { EnableCmd(hmenu, IDM_EDIT_CHAR2HEX, i); EnableCmd(hmenu, IDM_EDIT_HEX2CHAR, i); EnableCmd(hmenu, IDM_EDIT_SHOW_HEX, i); + EnableCmd(hmenu, IDM_EDIT_CODE_COMPRESS, i); + EnableCmd(hmenu, IDM_EDIT_CODE_PRETTY, i); EnableCmd(hmenu, IDM_EDIT_BASE64_ENCODE, i); EnableCmd(hmenu, IDM_EDIT_BASE64_SAFE_ENCODE, i); @@ -3772,6 +3776,13 @@ LRESULT MsgCommand(HWND hwnd, WPARAM wParam, LPARAM lParam) { EndWaitCursor(); break; + case IDM_EDIT_CODE_COMPRESS: + case IDM_EDIT_CODE_PRETTY: + BeginWaitCursor(); + EditFormatCode(LOWORD(wParam)); + EndWaitCursor(); + break; + case IDM_EDIT_BASE64_ENCODE: case IDM_EDIT_BASE64_SAFE_ENCODE: case IDM_EDIT_BASE64_HTML_EMBEDDED_IMAGE: