7573 lines
336 KiB
C++
7573 lines
336 KiB
C++
/*******************************************
|
|
* Zira Editor
|
|
* A lightweight PHP Editor
|
|
* (C)2019 https://github.com/ziracms/editor
|
|
*******************************************/
|
|
|
|
#include "editor.h"
|
|
#include "linenumber.h"
|
|
#include "linemark.h"
|
|
#include "linemap.h"
|
|
#include "breadcrumbs.h"
|
|
#include "completepopup.h"
|
|
#include "annotation.h"
|
|
#include "search.h"
|
|
#include <QScrollBar>
|
|
#include <QPainter>
|
|
#include <QPaintEvent>
|
|
#include <QTextBlock>
|
|
#include <QAbstractTextDocumentLayout>
|
|
#include <QTextStream>
|
|
#include <QMimeData>
|
|
#include <QShortcut>
|
|
#include <QMessageBox>
|
|
#include <QToolTip>
|
|
#include <QApplication>
|
|
#include <QTextLayout>
|
|
#include <QDesktopWidget>
|
|
#include <QTextDocumentFragment>
|
|
#include <QFileInfo>
|
|
#include <QDateTime>
|
|
#include <QMenu>
|
|
#include <QInputDialog>
|
|
#include <QAction>
|
|
#include <QScreen>
|
|
#include "math.h"
|
|
#include "helper.h"
|
|
#include "icon.h"
|
|
#include "spellchecker.h"
|
|
#include "scroller.h"
|
|
|
|
const std::string CRLF = "crlf";
|
|
const std::string CR = "cr";
|
|
const std::string LF = "lf";
|
|
|
|
const int INTERVAL_SCROLL_HIGHLIGHT_MILLISECONDS = 500;
|
|
const int INTERVAL_TEXT_CHANGED_MILLISECONDS = 200;
|
|
const int INTERVAL_CURSOR_POS_CHANGED_MILLISECONDS = 200;
|
|
const int INTERVAL_SPELL_CHECK_MILLISECONDS = 500;
|
|
|
|
const int SPELLCHECKER_INIT_BLOCKS_COUNT = 10;
|
|
|
|
const int TOOLTIP_OFFSET = 20;
|
|
const int TOOLTIP_SCREEN_MARGIN = 10;
|
|
|
|
const int LINE_NUMBER_WIDGET_PADDING = 20;
|
|
const int LINE_MARK_WIDGET_WIDTH = 20;
|
|
const int LINE_MARK_WIDGET_RECT_WIDTH = 10;
|
|
const int LINE_MARK_WIDGET_LINE_WIDTH = 2;
|
|
const int LINE_MAP_WIDGET_WIDTH = 20;
|
|
const int SEARCH_WIDGET_HEIGHT = 100;
|
|
const int BREADCRUMBS_WIDGET_HEIGHT = 21;
|
|
|
|
const int LINE_MAP_LINE_NUMBER_OFFSET = 10;
|
|
const int LINE_MAP_PROGRESS_WIDTH = 3;
|
|
const int LINE_MAP_PROGRESS_HEIGHT = 50;
|
|
|
|
const QString BREADCRUMBS_DELIMITER = " \u21e2 ";
|
|
|
|
const QString TOOLTIP_DELIMITER = "[:OR:]";
|
|
const QString TOOLTIP_PAGER_TPL = " | <b>[<u>%1</u>/%2]</b>";
|
|
const QString TOOLTIP_COLOR_TPL = "<span style=\"background:%1;\"> </span>";
|
|
|
|
const int SEARCH_LIMIT = 10000;
|
|
const int BIG_FILE_SIZE = 512000;
|
|
const int TOO_BIG_FILE_SIZE = 1048576;
|
|
|
|
const int LONG_LINE_CHARS_COUNT = 72;
|
|
const int FIRST_BLOCK_BIN_SEARCH_SCROLL_VALUE = 300;
|
|
|
|
const QString SNIPPET_PREFIX = "Snippet: @";
|
|
|
|
Editor::Editor(QWidget * parent):
|
|
QTextEdit(parent), mousePressTimer(this)
|
|
{
|
|
setMinimumSize(0, 0);
|
|
setMaximumSize(16777215, 16777215);
|
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
setAcceptRichText(false);
|
|
setAcceptDrops(false);
|
|
|
|
//document()->setDocumentMargin(0);
|
|
|
|
searchDisplayOnTop = false;
|
|
#if defined(Q_OS_ANDROID)
|
|
if (Settings::get("enable_android_desktop_mode") != "yes") {
|
|
searchDisplayOnTop = true;
|
|
}
|
|
#endif
|
|
|
|
wrapLines = false;
|
|
std::string wrapLinesStr = Settings::get("editor_wrap_long_lines");
|
|
if (wrapLinesStr == "yes") wrapLines = true;
|
|
if (wrapLines) {
|
|
setLineWrapMode(LineWrapMode::FixedColumnWidth);
|
|
setLineWrapColumnOrWidth(LONG_LINE_CHARS_COUNT);
|
|
setWordWrapMode(QTextOption::WrapMode::WordWrap);
|
|
} else {
|
|
setLineWrapMode(LineWrapMode::NoWrap);
|
|
setWordWrapMode(QTextOption::WrapMode::NoWrap);
|
|
}
|
|
|
|
QString theme = QString::fromStdString(Settings::get("theme"));
|
|
|
|
//QFont generalFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
|
|
QFont generalFont = QApplication::font();
|
|
|
|
// editor font
|
|
std::string fontFamily = Settings::get("editor_font_family");
|
|
std::string fontSize = Settings::get("editor_font_size");
|
|
std::string popupFontSize = Settings::get("editor_popup_font_size");
|
|
std::string tooltipFontSize = Settings::get("editor_tooltip_font_size");
|
|
std::string breadcrumbsFontSize = Settings::get("editor_breadcrumbs_font_size");
|
|
|
|
if (fontFamily=="") {
|
|
QFont sysFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
|
editorFont.setFamily(sysFont.family());
|
|
editorFont.setStyleHint(QFont::Monospace);
|
|
editorPopupFont.setFamily(sysFont.family());
|
|
editorPopupFont.setStyleHint(QFont::Monospace);
|
|
editorTooltipFont.setFamily(sysFont.family());
|
|
editorTooltipFont.setStyleHint(QFont::Monospace);
|
|
editorBreadcrumbsFont.setFamily(sysFont.family());
|
|
editorBreadcrumbsFont.setStyleHint(QFont::Monospace);
|
|
} else {
|
|
editorFont.setStyleHint(QFont::Monospace);
|
|
editorFont.setFamily(QString::fromStdString(fontFamily));
|
|
editorPopupFont.setStyleHint(QFont::Monospace);
|
|
editorPopupFont.setFamily(QString::fromStdString(fontFamily));
|
|
editorTooltipFont.setStyleHint(QFont::Monospace);
|
|
editorTooltipFont.setFamily(QString::fromStdString(fontFamily));
|
|
editorBreadcrumbsFont.setStyleHint(QFont::Monospace);
|
|
editorBreadcrumbsFont.setFamily(QString::fromStdString(fontFamily));
|
|
}
|
|
editorFont.setPointSize(std::stoi(fontSize));
|
|
editorPopupFont.setPointSize(std::stoi(popupFontSize));
|
|
editorTooltipFont.setPointSize(std::stoi(tooltipFontSize));
|
|
editorBreadcrumbsFont.setPointSize(std::stoi(breadcrumbsFontSize));
|
|
|
|
// bold text issue workaround
|
|
editorFont.setStyleName("");
|
|
editorPopupFont.setStyleName("");
|
|
editorTooltipFont.setStyleName("");
|
|
editorBreadcrumbsFont.setStyleName("");
|
|
|
|
setFont(editorFont);
|
|
|
|
// updating font family if using fallback font
|
|
QFontInfo fontInfo(editorFont);
|
|
if (fontInfo.family() != editorFont.family()) {
|
|
std::unordered_map<std::string, std::string> sData;
|
|
sData["editor_font_family"] = fontInfo.family().toStdString();
|
|
Settings::change(sData);
|
|
// showing message in initMode after slots connection
|
|
}
|
|
|
|
// colors
|
|
std::string lineNumberBgColorStr = Settings::get("editor_line_number_bg_color");
|
|
std::string lineNumberColorStr = Settings::get("editor_line_number_color");
|
|
std::string lineNumberModifiedBgColorStr = Settings::get("editor_line_number_modified_bg_color");
|
|
std::string lineNumberModifiedColorStr = Settings::get("editor_line_number_modified_color");
|
|
std::string lineNumberDeletedBorderColorStr = Settings::get("editor_line_number_deleted_border_color");
|
|
std::string lineMarkBgColorStr = Settings::get("editor_line_mark_bg_color");
|
|
std::string lineMapBgColorStr = Settings::get("editor_line_map_bg_color");
|
|
std::string lineMapScrollBgColorStr = Settings::get("editor_line_map_scroll_bg_color");
|
|
std::string lineMapScrollAreaBgColorStr = Settings::get("editor_line_map_scroll_area_bg_color");
|
|
std::string searchBgColorStr = Settings::get("editor_search_bg_color");
|
|
std::string breadcrumbsBgColorStr = Settings::get("editor_breadcrumbs_bg_color");
|
|
std::string breadcrumbsWarningBgColorStr = Settings::get("editor_breadcrumbs_warning_bg_color");
|
|
std::string breadcrumbsErrorBgColorStr = Settings::get("editor_breadcrumbs_error_bg_color");
|
|
std::string breadcrumbsColorStr = Settings::get("editor_breadcrumbs_color");
|
|
std::string widgetBorderColorStr = Settings::get("editor_widget_border_color");
|
|
std::string indentGuideLineColorStr = Settings::get("editor_indent_guide_line_color");
|
|
std::string selectedLineBgColorStr = Settings::get("editor_selected_line_bg_color");
|
|
std::string selectedWordBgColorStr = Settings::get("editor_selected_word_bg_color");
|
|
std::string selectedCharBgColorStr = Settings::get("editor_selected_char_bg_color");
|
|
std::string selectedCharColorStr = Settings::get("editor_selected_char_color");
|
|
std::string selectedTagBgColorStr = Settings::get("editor_selected_tag_bg_color");
|
|
std::string selectedExpressionBgColorStr = Settings::get("editor_selected_expression_bg_color");
|
|
std::string searchWordBgColorStr = Settings::get("editor_search_word_bg_color");
|
|
std::string searchWordColorStr = Settings::get("editor_search_word_color");
|
|
std::string selectionBgColorStr = Settings::get("editor_selection_bg_color");
|
|
std::string selectionColorStr = Settings::get("editor_selection_color");
|
|
std::string searchInputBgColorStr = Settings::get("editor_search_input_bg_color");
|
|
std::string searchInputErrorBgColorStr = Settings::get("editor_search_input_error_bg_color");
|
|
std::string lineMarkColorStr = Settings::get("editor_line_mark_color");
|
|
std::string lineErrorColorStr = Settings::get("editor_line_error_color");
|
|
std::string lineWarningColorStr = Settings::get("editor_line_warning_color");
|
|
std::string lineMarkRectColorStr = Settings::get("editor_line_mark_rect_color");
|
|
std::string lineErrorRectColorStr = Settings::get("editor_line_error_rect_color");
|
|
std::string lineWarningRectColorStr = Settings::get("editor_line_warning_rect_color");
|
|
std::string textColorStr = Settings::get("editor_text_color");
|
|
std::string bgColorStr = Settings::get("editor_bg_color");
|
|
std::string progressColorStr = Settings::get("progress_color");
|
|
|
|
lineNumberBgColor = QColor(lineNumberBgColorStr.c_str());
|
|
lineNumberColor = QColor(lineNumberColorStr.c_str());
|
|
lineNumberModifiedBgColor = QColor(lineNumberModifiedBgColorStr.c_str());
|
|
lineNumberModifiedColor = QColor(lineNumberModifiedColorStr.c_str());
|
|
lineNumberDeletedBorderColor = QColor(lineNumberDeletedBorderColorStr.c_str());
|
|
lineMarkBgColor = QColor(lineMarkBgColorStr.c_str());
|
|
lineMapBgColor = QColor(lineMapBgColorStr.c_str());
|
|
lineMapScrollBgColor = QColor(lineMapScrollBgColorStr.c_str());
|
|
lineMapScrollAreaBgColor = QColor(lineMapScrollAreaBgColorStr.c_str());
|
|
searchBgColor = QColor(searchBgColorStr.c_str());
|
|
breadcrumbsBgColor = QColor(breadcrumbsBgColorStr.c_str());
|
|
breadcrumbsWarningBgColor = QColor(breadcrumbsWarningBgColorStr.c_str());
|
|
breadcrumbsErrorBgColor = QColor(breadcrumbsErrorBgColorStr.c_str());
|
|
breadcrumbsColor = QColor(breadcrumbsColorStr.c_str());
|
|
widgetBorderColor = QColor(widgetBorderColorStr.c_str());
|
|
indentGuideLineColor = QColor(indentGuideLineColorStr.c_str());
|
|
selectedLineBgColor = QColor(selectedLineBgColorStr.c_str());
|
|
selectedWordBgColor = QColor(selectedWordBgColorStr.c_str());
|
|
selectedCharBgColor = QColor(selectedCharBgColorStr.c_str());
|
|
selectedCharColor = QColor(selectedCharColorStr.c_str());
|
|
selectedTagBgColor = QColor(selectedTagBgColorStr.c_str());
|
|
selectedExpressionBgColor = QColor(selectedExpressionBgColorStr.c_str());
|
|
searchWordBgColor = QColor(searchWordBgColorStr.c_str());
|
|
searchWordColor = QColor(searchWordColorStr.c_str());
|
|
selectionBgColor = QColor(selectionBgColorStr.c_str());
|
|
selectionColor = QColor(selectionColorStr.c_str());
|
|
searchInputBgColor = QColor(searchInputBgColorStr.c_str());
|
|
searchInputErrorBgColor = QColor(searchInputErrorBgColorStr.c_str());
|
|
lineMarkColor = QColor(lineMarkColorStr.c_str());
|
|
lineErrorColor = QColor(lineErrorColorStr.c_str());
|
|
lineWarningColor = QColor(lineWarningColorStr.c_str());
|
|
lineMarkRectColor = QColor(lineMarkRectColorStr.c_str());
|
|
lineErrorRectColor = QColor(lineErrorRectColorStr.c_str());
|
|
lineWarningRectColor = QColor(lineWarningRectColorStr.c_str());
|
|
textColor = QColor(textColorStr.c_str());
|
|
bgColor = QColor(bgColorStr.c_str());
|
|
progressColor = QColor(progressColorStr.c_str());
|
|
|
|
QPalette p;
|
|
p.setColor(QPalette::Base, bgColor);
|
|
p.setColor(QPalette::Text, textColor);
|
|
setPalette(p);
|
|
|
|
// tab width
|
|
tabWidthStr = Settings::get("editor_tab_width");
|
|
tabTypeStr = Settings::get("editor_tab_type");
|
|
detectTabTypeStr = Settings::get("editor_tab_type_detect");
|
|
setTabsSettings();
|
|
|
|
// new lines
|
|
std::string newLineModeStr = Settings::get("editor_new_line_mode");
|
|
if (newLineModeStr != CRLF && newLineModeStr != CR && newLineModeStr != LF) {
|
|
newLineMode = LF;
|
|
} else {
|
|
newLineMode = newLineModeStr;
|
|
}
|
|
|
|
// encoding
|
|
encoding = Settings::get("editor_encoding");
|
|
encodingFallback = Settings::get("editor_fallback_encoding");
|
|
|
|
// regexps
|
|
tagExpr = QRegularExpression("<(?:[/])?[a-zA-Z][^<>]*>");
|
|
tagOpenExpr = QRegularExpression("<([a-zA-Z][a-zA-Z0-9]*)[^<>]*>");
|
|
phpExpr = QRegularExpression("<[?].+?[?]>");
|
|
strExpr = QRegularExpression("([\"']).*?(\\1)");
|
|
functionExpr = QRegularExpression("\\bfunction\\b[\\s]+(?:[&][\\s]*)?([a-zA-Z_][a-zA-Z0-9_]*)[\\s]*[(]([^{]*)[)](?:[\\s]*[:][\\s]*((?:[\\?][\\s]*)?[a-zA-Z_][a-zA-Z0-9_]*))?");
|
|
tagExprIf = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?if[\\s]*[(].*?[)][\\s]*[:][\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprElse = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?else[\\s]*[:][\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprEndif = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?endif[\\s]*[;]*[\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprSwitch = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?switch[\\s]*[(].*?[)][\\s]*[:][\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprCase = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?case[\\s]*[a-zA-Z0-9_\"']+[\\s]*[:][\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprEndswitch = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?endswitch[\\s]*[;]*[\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprWhile = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?while[\\s]*[(].*?[)][\\s]*[:][\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprEndwhile = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?endwhile[\\s]*[;]*[\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprFor = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?for[\\s]*[(].*?[)][\\s]*[:][\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprEndfor = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?endfor[\\s]*[;]*[\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprForeach = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?foreach[\\s]*[(].*?[)][\\s]*[:][\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
tagExprEndforeach = QRegularExpression("<\\?(?:php)?[\\s]+(?:[/][*].*?[*][/][\\s]*)?endforeach[\\s]*[;]*[\\s]*(?:[/][*].*?[*][/][\\s]*)?(?:[/][/].*?)?\\?>");
|
|
stripTagsExpr = QRegularExpression("<[a-zA-Z/][^<>]*?>");
|
|
functionNameExpr = QRegularExpression("\\b([a-zA-Z0-9_]+)\\b[\\s]*[(]");
|
|
functionParamsExpr = QRegularExpression("^([^(]+)[(](.*)[)](.*)$");
|
|
functionWordExpr = QRegularExpression("(?:^|[^a-zA-Z0-9_\\$]+)function(?:[^a-zA-Z0-9_]+|$)");
|
|
classNameExpr = QRegularExpression("([a-zA-Z0-9_\\\\]+)[\\s]*[(]");
|
|
colorExpr = QRegularExpression("^[#](?:[a-fA-F0-9][a-fA-F0-9][a-fA-F0-9])(?:[a-fA-F0-9][a-fA-F0-9][a-fA-F0-9])?(?:[a-fA-F0-9][a-fA-F0-9])?$");
|
|
spellWordExpr = QRegularExpression("([\\p{L}0-9_'\\$\\-]+)");
|
|
|
|
// some features is enabled only in experimental mode
|
|
experimentalMode = false;
|
|
#if !defined(Q_OS_ANDROID)
|
|
std::string experimentalModeStr = Settings::get("experimental_mode_enabled");
|
|
if (experimentalModeStr == "yes") experimentalMode = true;
|
|
#endif
|
|
|
|
// highlighter
|
|
highlight = new Highlight(document());
|
|
std::string unusedVariableColorStr = Settings::get("highlight_unused_variable_color");
|
|
unusedVariableColor = QColor(unusedVariableColorStr.c_str());
|
|
connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(contentsChange(int,int,int)));
|
|
connect(highlight, SIGNAL(progressChanged(int)), this, SLOT(highlightProgressChanged(int)));
|
|
|
|
// update area slots
|
|
connect(this->document(), SIGNAL(blockCountChanged(int)), this, SLOT(blockCountChanged(int)));
|
|
connect(this->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(verticalScrollbarValueChanged(int)));
|
|
connect(this->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(horizontalScrollbarValueChanged(int)));
|
|
connect(this, SIGNAL(textChanged()), this, SLOT(textChanged()));
|
|
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged()));
|
|
|
|
// shortcuts
|
|
QString shortcutBackTabStr = QString::fromStdString(Settings::get("shortcut_backtab"));
|
|
QShortcut * shortcutShiftTab = new QShortcut(QKeySequence(shortcutBackTabStr), this);
|
|
shortcutShiftTab->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutShiftTab, SIGNAL(activated()), this, SLOT(backtab()));
|
|
|
|
QString shortcutSaveStr = QString::fromStdString(Settings::get("shortcut_save"));
|
|
QShortcut * shortcutSave = new QShortcut(QKeySequence(shortcutSaveStr), this);
|
|
shortcutSave->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutSave, SIGNAL(activated()), this, SLOT(save()));
|
|
|
|
QString shortcutCommentStr = QString::fromStdString(Settings::get("shortcut_comment"));
|
|
QShortcut * shortcutComment = new QShortcut(QKeySequence(shortcutCommentStr), this);
|
|
shortcutComment->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutComment, SIGNAL(activated()), this, SLOT(comment()));
|
|
|
|
QString shortcutOverwriteModeStr = QString::fromStdString(Settings::get("shortcut_overwrite_mode"));
|
|
QShortcut * shortcutOverwriteMode = new QShortcut(QKeySequence(shortcutOverwriteModeStr), this);
|
|
shortcutOverwriteMode->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutOverwriteMode, SIGNAL(activated()), this, SLOT(switchOverwrite()));
|
|
|
|
QString shortcutTooltipStr = QString::fromStdString(Settings::get("shortcut_tooltip"));
|
|
QShortcut * shortcutTooltip = new QShortcut(QKeySequence(shortcutTooltipStr), this);
|
|
shortcutTooltip->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutTooltip, SIGNAL(activated()), this, SLOT(tooltip()));
|
|
|
|
QString shortcutSearchStr = QString::fromStdString(Settings::get("shortcut_search"));
|
|
QShortcut * shortcutSearch = new QShortcut(QKeySequence(shortcutSearchStr), this);
|
|
shortcutSearch->setContext(Qt::WidgetWithChildrenShortcut);
|
|
connect(shortcutSearch, SIGNAL(activated()), this, SLOT(findToggle()));
|
|
|
|
QString shortcutSelectWordStr = QString::fromStdString(Settings::get("shortcut_select_word"));
|
|
QShortcut * shortcutSelectWord = new QShortcut(QKeySequence(shortcutSelectWordStr), this);
|
|
shortcutSelectWord->setContext(Qt::WidgetWithChildrenShortcut);
|
|
connect(shortcutSelectWord, SIGNAL(activated()), this, SLOT(selectWord()));
|
|
|
|
QString shortcutMultiSelectStr = QString::fromStdString(Settings::get("shortcut_multiselect"));
|
|
QShortcut * shortcutMultiSelect = new QShortcut(QKeySequence(shortcutMultiSelectStr), this);
|
|
shortcutMultiSelect->setContext(Qt::WidgetWithChildrenShortcut);
|
|
connect(shortcutMultiSelect, SIGNAL(activated()), this, SLOT(multiSelectToggle()));
|
|
|
|
QString shortcutHelpStr = QString::fromStdString(Settings::get("shortcut_help"));
|
|
QShortcut * shortcutHelp = new QShortcut(QKeySequence(shortcutHelpStr), this);
|
|
shortcutHelp->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutHelp, SIGNAL(activated()), this, SLOT(showHelpRequested()));
|
|
|
|
QString shortcutDuplicateStr = QString::fromStdString(Settings::get("shortcut_duplicate_line"));
|
|
QShortcut * shortcutDuplicate = new QShortcut(QKeySequence(shortcutDuplicateStr), this);
|
|
shortcutDuplicate->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutDuplicate, SIGNAL(activated()), this, SLOT(duplicateLine()));
|
|
|
|
QString shortcutDeleteStr = QString::fromStdString(Settings::get("shortcut_delete_line"));
|
|
QShortcut * shortcutDelete = new QShortcut(QKeySequence(shortcutDeleteStr), this);
|
|
shortcutDelete->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutDelete, SIGNAL(activated()), this, SLOT(deleteLine()));
|
|
|
|
QString shortcutContextMenuStr = QString::fromStdString(Settings::get("shortcut_context_menu"));
|
|
QShortcut * shortcutContextMenu = new QShortcut(QKeySequence(shortcutContextMenuStr), this);
|
|
shortcutContextMenu->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutContextMenu, SIGNAL(activated()), this, SLOT(contextMenu()));
|
|
|
|
QString shortcutGoToStr = QString::fromStdString(Settings::get("shortcut_goto"));
|
|
QShortcut * shortcutGoTo = new QShortcut(QKeySequence(shortcutGoToStr), this);
|
|
shortcutGoTo->setContext(Qt::WidgetShortcut);
|
|
connect(shortcutGoTo, SIGNAL(activated()), this, SLOT(gotoLineRequest()));
|
|
|
|
// order matters
|
|
// annotation
|
|
lineAnnotation = new Annotation(this);
|
|
// line number area
|
|
lineNumber = new LineNumber(this);
|
|
// line mark area
|
|
lineMark = new LineMark(this);
|
|
// line map area
|
|
lineMap = new LineMap(this);
|
|
|
|
// breadcrumbs
|
|
breadcrumbs = new Breadcrumbs(this);
|
|
breadcrumbs->setFont(editorBreadcrumbsFont);
|
|
showBreadcrumbs = false;
|
|
std::string showBreadcrumbsStr = Settings::get("editor_breadcrumbs_enabled");
|
|
if (showBreadcrumbsStr == "yes") showBreadcrumbs = true;
|
|
|
|
qaBtn = new QToolButton(breadcrumbs);
|
|
qaBtn->setIcon(QIcon(":/icons/separator-double.png"));
|
|
//qaBtn->setIconSize(QSize(breadcrumbs->height(), breadcrumbs->height()));
|
|
qaBtn->setToolTip(tr("Quick Access"));
|
|
qaBtn->setProperty("BreadcrumbsButton", "QuickAccess");
|
|
if (theme == THEME_SYSTEM || theme.indexOf(STYLE_PLUGIN_DISPLAY_NAME_SUFFIX) > 0) {
|
|
qaBtn->setStyleSheet("border:none");
|
|
qaBtn->setCursor(Qt::PointingHandCursor);
|
|
}
|
|
connect(qaBtn, SIGNAL(pressed()), this, SLOT(qaBtnClicked()));
|
|
|
|
// complete popup
|
|
completePopup = new CompletePopup(this);
|
|
completePopup->setFont(editorPopupFont);
|
|
connect(completePopup, SIGNAL(itemDataClicked(QString, QString)), this, SLOT(completePopupSelected(QString, QString)));
|
|
|
|
// search widget
|
|
search = new Search(this);
|
|
search->setFont(generalFont);
|
|
QPalette searchPallete = search->palette();
|
|
searchPallete.setColor(QPalette::Foreground, textColor);
|
|
searchPallete.setColor(QPalette::Base, searchBgColor.lighter(110));
|
|
searchPallete.setColor(QPalette::ButtonText, textColor);
|
|
searchPallete.setColor(QPalette::Button, searchBgColor.lighter(110));
|
|
search->setPalette(searchPallete);
|
|
searchCaSe = false;
|
|
searchWord = false;
|
|
searchRegE = false;
|
|
searchString = "";
|
|
|
|
extension = "";
|
|
modeOnKeyPress = "";
|
|
lastKeyPressed = -1;
|
|
lastKeyPressedBlockNumber = -1;
|
|
tooltipSavedText = "";
|
|
tooltipSavedList.clear();
|
|
tooltipSavedPageOffset = -1;
|
|
tooltipSavedOrigName = "";
|
|
tooltipSavedBlockNumber = -1;
|
|
|
|
focused = false;
|
|
cursorPositionChangeLocked = false;
|
|
scrollBarValueChangeLocked = false;
|
|
overwrite = false;
|
|
tabIndex = -1;
|
|
parseLocked = false;
|
|
isUndoAvailable = false;
|
|
isRedoAvailable = false;
|
|
lastCursorPositionBlockNumber = -1;
|
|
isBigFile = false;
|
|
isMultiSelectMode = false;
|
|
multiSelectString = "";
|
|
multiSelectInsertText = "";
|
|
multiSelectCursors.clear();
|
|
multiSelectCursor = QTextCursor(document());
|
|
ignoreMouseRelease = false;
|
|
|
|
connect(this, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool)));
|
|
connect(this, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool)));
|
|
|
|
// tooltip label
|
|
tooltipLabel.setFont(editorTooltipFont);
|
|
|
|
tooltipBoldColorStr = QString::fromStdString(Settings::get("editor_tooltip_bold_color"));
|
|
tooltipBoldTagStart = "<b style=\"color:"+tooltipBoldColorStr+"\">";
|
|
tooltipBoldTagEnd = "</b>";
|
|
|
|
CW = &CompleteWords::instance();
|
|
HW = &HighlightWords::instance();
|
|
HPW = &HelpWords::instance();
|
|
SW = &SpellWords::instance();
|
|
SNP = &Snippets::instance();
|
|
|
|
parsePHPEnabled = false;
|
|
std::string parsePHPEnabledStr = Settings::get("parser_enable_parse_php");
|
|
if (parsePHPEnabledStr == "yes") parsePHPEnabled = true;
|
|
|
|
parseJSEnabled = false;
|
|
std::string parseJSEnabledStr = Settings::get("parser_enable_parse_js");
|
|
if (parseJSEnabledStr == "yes") parseJSEnabled = true;
|
|
|
|
parseCSSEnabled = false;
|
|
std::string parseCSSEnabledStr = Settings::get("parser_enable_parse_css");
|
|
if (parseCSSEnabledStr == "yes") parseCSSEnabled = true;
|
|
|
|
cleanBeforeSave = false;
|
|
std::string cleanBeforeSaveStr = Settings::get("editor_clean_before_save");
|
|
if (cleanBeforeSaveStr == "yes") cleanBeforeSave = true;
|
|
|
|
annotationsEnabled = false;
|
|
std::string annotationsEnabledStr = Settings::get("editor_show_annotations");
|
|
if (annotationsEnabledStr == "yes") annotationsEnabled = true;
|
|
|
|
int parseResultChangedDelayMS = std::stoi(Settings::get("editor_parse_interval"));
|
|
if (parseResultChangedDelayMS >= 1000) parseResultChangedDelay = parseResultChangedDelayMS;
|
|
else parseResultChangedDelay = 5000;
|
|
|
|
spellCheckerEnabled = false;
|
|
std::string spellCheckerEnabledStr = Settings::get("spellchecker_enabled");
|
|
if (spellCheckerEnabledStr == "yes") spellCheckerEnabled = true;
|
|
spellChecker = SpellChecker::instance().getSpellChecker();
|
|
|
|
drawLongLineMarker = false;
|
|
std::string drawLongLineMarkerStr = Settings::get("editor_long_line_marker_enabled");
|
|
if (drawLongLineMarkerStr == "yes") drawLongLineMarker = true;
|
|
|
|
drawIndentGuideLines = false;
|
|
std::string drawIndentGuideLinesStr = Settings::get("editor_indent_guide_lines_enabled");
|
|
if (drawIndentGuideLinesStr == "yes") drawIndentGuideLines = true;
|
|
|
|
// cursor is not set to default sometimes
|
|
horizontalScrollBar()->setCursor(Qt::ArrowCursor);
|
|
verticalScrollBar()->setCursor(Qt::ArrowCursor);
|
|
|
|
// for Android
|
|
inputEventKey = -1;
|
|
setInputMethodHints(Qt::ImhNoPredictiveText);
|
|
mousePressTimer.setInterval(1000);
|
|
mousePressTimer.setSingleShot(true);
|
|
connect(&mousePressTimer, SIGNAL(timeout()), this, SLOT(contextMenu()));
|
|
|
|
isGesturesEnabled = false;
|
|
#if defined(Q_OS_ANDROID)
|
|
// scrolling by gesture
|
|
if (Settings::get("enable_android_gestures") == "yes" && Settings::get("editor_enable_android_gestures") == "yes") {
|
|
enableGestures();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Editor::~Editor()
|
|
{
|
|
delete highlight;
|
|
}
|
|
|
|
void Editor::init()
|
|
{
|
|
setMouseTracking(true);
|
|
updateViewportMargins();
|
|
}
|
|
|
|
void Editor::setTabIndex(int index)
|
|
{
|
|
tabIndex = index;
|
|
}
|
|
|
|
int Editor::getTabIndex()
|
|
{
|
|
return tabIndex;
|
|
}
|
|
|
|
void Editor::setTabsSettings()
|
|
{
|
|
tabWidth = std::stoi(tabWidthStr);
|
|
QFontMetrics fm(editorFont);
|
|
/* QTextEdit::setTabStopWidth() is deprecated */
|
|
/*
|
|
setTabStopWidth(tabWidth * fm.width(' '));
|
|
*/
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
setTabStopDistance(tabWidth * fm.width(' '));
|
|
*/
|
|
setTabStopDistance(tabWidth * fm.horizontalAdvance(" "));
|
|
tabType = tabTypeStr;
|
|
tabWidthPixels = 0;
|
|
if (detectTabTypeStr == "yes") detectTabType = true;
|
|
else detectTabType = false;
|
|
}
|
|
|
|
void Editor::reset()
|
|
{
|
|
setReadOnly(true);
|
|
highlight->resetMode();
|
|
fileName = "";
|
|
extension = "";
|
|
highlighterInitialized = false;
|
|
is_ready = false;
|
|
cursorPositionChangeLocked = false;
|
|
scrollBarValueChangeLocked = false;
|
|
textChangeLocked = false;
|
|
modeOnKeyPress = "";
|
|
lastKeyPressed = -1;
|
|
lastKeyPressedBlockNumber = -1;
|
|
tooltipSavedText = "";
|
|
tooltipSavedList.clear();
|
|
tooltipSavedPageOffset = -1;
|
|
tooltipSavedOrigName = "";
|
|
tooltipSavedBlockNumber = -1;
|
|
setTabsSettings();
|
|
hidePopups();
|
|
static_cast<LineMap *>(lineMap)->clear();
|
|
static_cast<LineMark *>(lineMark)->clear();
|
|
markPoints.clear();
|
|
modifiedLines.clear();
|
|
modified = false;
|
|
lastModifiedMsec = 0;
|
|
warningDisplayed = false;
|
|
parseLocked = false;
|
|
isUndoAvailable = false;
|
|
isRedoAvailable = false;
|
|
backPositions.clear();
|
|
forwardPositions.clear();
|
|
lastCursorPositionBlockNumber = -1;
|
|
isParseError = false;
|
|
gitAnnotationLastLineNumber = -1;
|
|
gitAnnotations.clear();
|
|
gitDiffLines.clear();
|
|
spellLocked = false;
|
|
spellBlocksQueue.clear();
|
|
spellPastedBlocksQueue.clear();
|
|
errorsExtraSelections.clear();
|
|
spellCheckInitBlockNumber = 0;
|
|
highlightProgressPercent = 0;
|
|
spellProgressPercent = 0;
|
|
firstVisibleBlockIndex = -1;
|
|
lastVisibleBlockIndex = -1;
|
|
isMultiSelectMode = false;
|
|
multiSelectString = "";
|
|
multiSelectInsertText = "";
|
|
multiSelectCursors.clear();
|
|
ignoreMouseRelease = false;
|
|
}
|
|
|
|
void Editor::enableGestures()
|
|
{
|
|
Scroller::enableGestures(this);
|
|
isGesturesEnabled = true;
|
|
}
|
|
|
|
void Editor::disableGestures()
|
|
{
|
|
Scroller::disableGestures(this);
|
|
isGesturesEnabled = false;
|
|
}
|
|
|
|
void Editor::highlightProgressChanged(int percent)
|
|
{
|
|
highlightProgressPercent = percent;
|
|
if (highlightProgressPercent > 100) highlightProgressPercent = 100;
|
|
if (highlightProgressPercent < 0) highlightProgressPercent = 0;
|
|
lineMap->repaint();
|
|
}
|
|
|
|
void Editor::spellProgressChanged(int percent)
|
|
{
|
|
spellProgressPercent = percent;
|
|
if (spellProgressPercent > 100) spellProgressPercent = 100;
|
|
if (spellProgressPercent < 0) spellProgressPercent = 0;
|
|
lineMap->update();
|
|
}
|
|
|
|
void Editor::hidePopups()
|
|
{
|
|
hideCompletePopup();
|
|
hideTooltip();
|
|
}
|
|
|
|
void Editor::setFileName(QString name)
|
|
{
|
|
fileName = name;
|
|
QFileInfo fInfo(fileName);
|
|
QDateTime dtModified = fInfo.lastModified();
|
|
lastModifiedMsec = dtModified.time().msec();
|
|
highlight->setFileName(fInfo.baseName());
|
|
}
|
|
|
|
QString Editor::getFileName()
|
|
{
|
|
return fileName;
|
|
}
|
|
|
|
bool Editor::isModified()
|
|
{
|
|
return modified;
|
|
}
|
|
|
|
void Editor::setModified(bool m)
|
|
{
|
|
modified = m;
|
|
emit modifiedStateChanged(tabIndex, modified);
|
|
}
|
|
|
|
void Editor::setParseError(bool error)
|
|
{
|
|
isParseError = error;
|
|
}
|
|
|
|
bool Editor::getParseError()
|
|
{
|
|
return isParseError;
|
|
}
|
|
|
|
QString Editor::getFileExtension()
|
|
{
|
|
return extension;
|
|
}
|
|
|
|
void Editor::setIsBigFile(bool isBig)
|
|
{
|
|
isBigFile = isBig;
|
|
}
|
|
|
|
void Editor::initMode(QString ext)
|
|
{
|
|
extension = ext;
|
|
modified = false;
|
|
document()->setModified(modified);
|
|
emit modifiedStateChanged(tabIndex, modified);
|
|
updateWidgetsGeometry();
|
|
|
|
// check font
|
|
QFontInfo fontInfo(editorFont);
|
|
if (fontInfo.family() != editorFont.family()) {
|
|
//Helper::showMessage(QObject::tr("Font \"%1\" was not loaded. Using \"%2\" font family.").arg(editorFont.family()).arg(fontInfo.family()));
|
|
emit showPopupText(tabIndex, QObject::tr("Font \"%1\" was not loaded. Using \"%2\" font family.").arg(editorFont.family()).arg(fontInfo.family()));
|
|
}
|
|
}
|
|
|
|
void Editor::initHighlighter()
|
|
{
|
|
if (highlighterInitialized) return;
|
|
highlighterInitialized = true;
|
|
|
|
highlight->setIsBigFile(isBigFile);
|
|
highlight->initMode(extension, getLastVisibleBlockIndex());
|
|
|
|
bool isFocused = hasFocus();
|
|
int line = getCursorLine();
|
|
highlight->setFirstRunMode(true);
|
|
highlight->rehighlight();
|
|
highlight->setFirstRunMode(false);
|
|
if (line > 1) gotoLine(line, false);
|
|
if (isFocused) setFocus();
|
|
|
|
setReadOnly(false);
|
|
emit statusBarText(tabIndex, "");
|
|
is_ready = true;
|
|
cursorPositionChangedDelayed();
|
|
emit ready(tabIndex);
|
|
if (highlight->getModeType() == MODE_MIXED) highlightUnusedVars(false);
|
|
initSpellChecker();
|
|
}
|
|
|
|
void Editor::initSpellChecker()
|
|
{
|
|
if (!spellCheckerEnabled || spellChecker == nullptr || isBigFile) return;
|
|
int totalBlocks = document()->blockCount();
|
|
while(spellCheckInitBlockNumber < totalBlocks) {
|
|
for (int i=0; i<SPELLCHECKER_INIT_BLOCKS_COUNT; i++) {
|
|
spellBlocksQueue.append(i+spellCheckInitBlockNumber);
|
|
}
|
|
spellCheckInitBlockNumber += SPELLCHECKER_INIT_BLOCKS_COUNT;
|
|
spellCheck(false, false);
|
|
int percent = (spellCheckInitBlockNumber * 100) / totalBlocks;
|
|
if (percent - spellProgressPercent > 10) spellProgressChanged(percent);
|
|
QCoreApplication::processEvents();
|
|
if (tabIndex < 0) break;
|
|
}
|
|
spellProgressChanged(100);
|
|
}
|
|
|
|
void Editor::spellCheckPasted()
|
|
{
|
|
if (!spellCheckerEnabled || spellChecker == nullptr || isBigFile) return;
|
|
if (tabIndex < 0) return;
|
|
if (spellPastedBlocksQueue.size() == 0) return;
|
|
spellBlocksQueue.append(spellPastedBlocksQueue.last());
|
|
spellCheck(false, false);
|
|
spellPastedBlocksQueue.removeLast();
|
|
if (spellPastedBlocksQueue.size() > 0) {
|
|
QTimer::singleShot(INTERVAL_SPELL_CHECK_MILLISECONDS, this, SLOT(spellCheckPasted()));
|
|
}
|
|
}
|
|
|
|
std::string Editor::getModeType()
|
|
{
|
|
return highlight->getModeType();
|
|
}
|
|
|
|
bool Editor::isReady()
|
|
{
|
|
return is_ready;
|
|
}
|
|
|
|
void Editor::paintEvent(QPaintEvent * event)
|
|
{
|
|
if (highlight->isDirty()) return;
|
|
|
|
QPainter painter(viewport());
|
|
|
|
if (drawIndentGuideLines && tabWidthPixels > 0) {
|
|
QPen pen(indentGuideLineColor);
|
|
pen.setStyle(Qt::SolidLine);
|
|
painter.setPen(pen);
|
|
|
|
int blockNumber = getFirstVisibleBlockIndex();
|
|
if (blockNumber < 0) return;
|
|
if (blockNumber>0) blockNumber--;
|
|
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
//int top = contentsMargins().top();
|
|
int top = 0; // widget has offset
|
|
|
|
if (blockNumber == 0) {
|
|
top += static_cast<int>(document()->documentMargin()) - verticalScrollBar()->sliderPosition();
|
|
} else {
|
|
QTextBlock prev_block = document()->findBlockByNumber(blockNumber-1);
|
|
int prev_y = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).y());
|
|
int prev_h = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).height());
|
|
top += prev_y + prev_h - verticalScrollBar()->sliderPosition();
|
|
}
|
|
|
|
int bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
|
|
int scrollX = horizontalScrollBar()->value();
|
|
int prevBraces = 0;
|
|
|
|
while (block.isValid() && top <= event->rect().bottom()) {
|
|
if (block.isVisible()) {
|
|
int braces = 0;
|
|
QString blockText = block.text();
|
|
if (blockText.size() != 0) {
|
|
QString prefix = "";
|
|
for (int i=0; i<blockText.size(); i++) {
|
|
QChar c = blockText[i];
|
|
if (iswspace(c.toLatin1())) {
|
|
prefix += c;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (prefix.size() > 0) {
|
|
prefix = prefix.replace("\t", QString(" ").repeated(tabWidth));
|
|
braces = static_cast<int>(ceil(prefix.size() * 1.0 / tabWidth) - 1);
|
|
}
|
|
prevBraces = braces;
|
|
} else {
|
|
braces = prevBraces;
|
|
}
|
|
if (braces > 0) {
|
|
for (int i=1; i <= braces; i++) {
|
|
painter.drawLine(document()->documentMargin() + tabWidthPixels*i-scrollX, top, document()->documentMargin() + tabWidthPixels*i-scrollX, bottom);
|
|
}
|
|
}
|
|
}
|
|
block = block.next();
|
|
top = bottom;
|
|
bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
blockNumber++;
|
|
}
|
|
}
|
|
|
|
if (drawLongLineMarker) {
|
|
QFontMetrics fm(font());
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
int longMarkerX = fm.width(QString("w").repeated(LONG_LINE_CHARS_COUNT));
|
|
*/
|
|
int longMarkerX = fm.horizontalAdvance(QString("w").repeated(LONG_LINE_CHARS_COUNT));
|
|
int scrollX = horizontalScrollBar()->value();
|
|
|
|
QPen pen(widgetBorderColor);
|
|
pen.setStyle(Qt::DotLine);
|
|
painter.setPen(pen);
|
|
painter.drawLine(longMarkerX-scrollX, 0, longMarkerX-scrollX, viewport()->geometry().height());
|
|
}
|
|
|
|
QTextEdit::paintEvent(event);
|
|
}
|
|
|
|
void Editor::setParseResult(ParsePHP::ParseResult result)
|
|
{
|
|
parseResultPHP = result;
|
|
parseLocked = false;
|
|
}
|
|
|
|
void Editor::setParseResult(ParseJS::ParseResult result)
|
|
{
|
|
parseResultJS = result;
|
|
parseLocked = false;
|
|
}
|
|
|
|
void Editor::setParseResult(ParseCSS::ParseResult result)
|
|
{
|
|
parseResultCSS = result;
|
|
parseLocked = false;
|
|
}
|
|
|
|
void Editor::setGitAnnotations(QHash<int, Git::Annotation> annotations)
|
|
{
|
|
if (warningDisplayed) return;
|
|
gitAnnotations = annotations;
|
|
gitAnnotationLastLineNumber = -1;
|
|
showLineAnnotation();
|
|
}
|
|
|
|
void Editor::setGitDiffLines(QHash<int, Git::DiffLine> mLines)
|
|
{
|
|
if (warningDisplayed) return;
|
|
gitDiffLines = mLines;
|
|
updateLineWidgetsArea();
|
|
}
|
|
|
|
int Editor::lineNumberAreaWidth()
|
|
{
|
|
int digits = 1;
|
|
int max = qMax(1, document()->blockCount());
|
|
while (max >= 10) {
|
|
max /= 10;
|
|
++digits;
|
|
}
|
|
|
|
QFontMetrics fm(editorFont);
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
int w = fm.width("0");
|
|
*/
|
|
int w = fm.horizontalAdvance("0");
|
|
int space = LINE_NUMBER_WIDGET_PADDING + w * digits;
|
|
return space;
|
|
}
|
|
|
|
int Editor::lineMarkAreaWidth()
|
|
{
|
|
return LINE_MARK_WIDGET_WIDTH;
|
|
}
|
|
|
|
int Editor::lineMapAreaWidth()
|
|
{
|
|
return LINE_MAP_WIDGET_WIDTH;
|
|
}
|
|
|
|
int Editor::searchWidgetHeight()
|
|
{
|
|
int scrollH = 0;
|
|
int scrollHE = 0;
|
|
if (static_cast<Search *>(search)->scrollAreaHorizontalScrollBar()->maximum() > static_cast<Search *>(search)->scrollAreaHorizontalScrollBar()->minimum()) {
|
|
scrollH = static_cast<Search *>(search)->scrollAreaHorizontalScrollBar()->height();
|
|
}
|
|
if (!searchDisplayOnTop && static_cast<Search *>(search)->horizontalScrollBar()->maximum() > static_cast<Search *>(search)->horizontalScrollBar()->minimum()) {
|
|
scrollHE = static_cast<Search *>(search)->horizontalScrollBar()->height();
|
|
}
|
|
return SEARCH_WIDGET_HEIGHT + scrollH + scrollHE;
|
|
}
|
|
|
|
int Editor::breadcrumbsHeight()
|
|
{
|
|
if (!showBreadcrumbs) return 0;
|
|
if (editorBreadcrumbsFont.pointSize() > 10) {
|
|
return editorBreadcrumbsFont.pointSize() + 11;
|
|
}
|
|
return BREADCRUMBS_WIDGET_HEIGHT;
|
|
}
|
|
|
|
void Editor::updateViewportMargins()
|
|
{
|
|
int lineW = lineNumberAreaWidth();
|
|
int markW = lineMarkAreaWidth();
|
|
int mapW = lineMapAreaWidth();
|
|
int searchH = searchWidgetHeight();
|
|
if (!search->isVisible()) searchH = 0;
|
|
int breadcrumbsH = breadcrumbsHeight();
|
|
if (!searchDisplayOnTop) {
|
|
setViewportMargins(lineW + markW, breadcrumbsH, mapW, searchH);
|
|
} else {
|
|
setViewportMargins(lineW + markW, breadcrumbsH + searchH, mapW, 0);
|
|
}
|
|
}
|
|
|
|
void Editor::updateLineWidgetsArea()
|
|
{
|
|
lineNumber->update();
|
|
lineMark->update();
|
|
}
|
|
|
|
void Editor::updateLineAnnotationView()
|
|
{
|
|
if (!annotationsEnabled) return;
|
|
if (static_cast<Annotation *>(lineAnnotation)->getText().size() == 0) return;
|
|
|
|
int blockNumber = getFirstVisibleBlockIndex();
|
|
if (blockNumber < 0) return;
|
|
if (blockNumber>0) blockNumber--;
|
|
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
int top = contentsMargins().top();
|
|
|
|
if (blockNumber == 0) {
|
|
top += static_cast<int>(document()->documentMargin()) - verticalScrollBar()->sliderPosition();
|
|
} else {
|
|
QTextBlock prev_block = document()->findBlockByNumber(blockNumber-1);
|
|
int prev_y = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).y());
|
|
int prev_h = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).height());
|
|
top += prev_y + prev_h - verticalScrollBar()->sliderPosition();
|
|
}
|
|
|
|
int bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
|
|
while (block.isValid()) {
|
|
if (block.isVisible() && block.blockNumber() == textCursor().block().blockNumber()) {
|
|
int y = top;
|
|
if (breadcrumbs->isVisible()) y += breadcrumbs->geometry().height();
|
|
int x = static_cast<int>(document()->documentLayout()->blockBoundingRect(block).width());
|
|
int h = bottom - top;
|
|
if (wrapLines && block.layout() != nullptr) {
|
|
x = static_cast<int>(block.layout()->lineAt(block.layout()->lineCount()-1).naturalTextWidth());
|
|
if (block.layout()->lineCount() > 1) {
|
|
y += h;
|
|
h = static_cast<int>(block.layout()->lineAt(block.layout()->lineCount()-1).height());
|
|
y -= h;
|
|
}
|
|
}
|
|
x += lineNumber->geometry().width() + lineMark->geometry().width();
|
|
x += ANNOTATION_LEFT_MARGIN;
|
|
QFontMetrics fm(font());
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
int tw = fm.width(static_cast<Annotation *>(lineAnnotation)->getText());
|
|
*/
|
|
int tw = fm.horizontalAdvance(static_cast<Annotation *>(lineAnnotation)->getText());
|
|
tw += h; // icon
|
|
int bw = geometry().width() - lineMap->geometry().width();
|
|
bw -= ANNOTATION_RIGHT_MARGIN;
|
|
if (x + tw < bw) x += bw - x - tw;
|
|
if (horizontalScrollBar()->isVisible()) x -= horizontalScrollBar()->value();
|
|
lineAnnotation->move(x, y);
|
|
static_cast<Annotation *>(lineAnnotation)->setSize(tw, h);
|
|
if (!lineAnnotation->isVisible()) static_cast<Annotation *>(lineAnnotation)->fadeIn();
|
|
return;
|
|
}
|
|
block = block.next();
|
|
top = bottom;
|
|
bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
blockNumber++;
|
|
}
|
|
lineAnnotation->hide();
|
|
}
|
|
|
|
void Editor::showLineAnnotation()
|
|
{
|
|
if (!annotationsEnabled || gitAnnotations.size() == 0) return;
|
|
QTextBlock block = textCursor().block();
|
|
int line = block.blockNumber() + 1;
|
|
if (gitAnnotationLastLineNumber == line) {
|
|
static_cast<Annotation *>(lineAnnotation)->setText("");
|
|
if (lineAnnotation->isVisible()) lineAnnotation->hide();
|
|
return;
|
|
}
|
|
QString annotationText = "";
|
|
int error = static_cast<LineMark *>(lineMark)->getError(line, annotationText);
|
|
if (error > 0 && annotationText.size() > 0) {
|
|
annotationText = annotationText.replace("\t", QString(" ").repeated(tabWidth));
|
|
} else if (gitAnnotations.contains(line)) {
|
|
Git::Annotation annotation = gitAnnotations.value(line);
|
|
annotationText = tr("git") + ": " + annotation.comment + " / " + annotation.committer + " [" + annotation.committerDate + "]";
|
|
}
|
|
if (annotationText.size() > 0) {
|
|
static_cast<Annotation *>(lineAnnotation)->setText(annotationText);
|
|
updateLineAnnotationView();
|
|
gitAnnotationLastLineNumber = line;
|
|
}
|
|
}
|
|
|
|
std::string Editor::getTabType()
|
|
{
|
|
return tabType;
|
|
}
|
|
|
|
int Editor::getTabWidth()
|
|
{
|
|
return tabWidth;
|
|
}
|
|
|
|
std::string Editor::getNewLineMode()
|
|
{
|
|
return newLineMode;
|
|
}
|
|
|
|
std::string Editor::getEncoding()
|
|
{
|
|
return encoding;
|
|
}
|
|
|
|
std::string Editor::getFallbackEncoding()
|
|
{
|
|
return encodingFallback;
|
|
}
|
|
bool Editor::isOverwrite()
|
|
{
|
|
return overwrite;
|
|
}
|
|
|
|
void Editor::detectTabsMode()
|
|
{
|
|
QFontMetrics fm(font());
|
|
if (!detectTabType) {
|
|
tabWidthPixels = tabType == "spaces" ? fm.horizontalAdvance(QString(" ").repeated(tabWidth)) : tabStopDistance();
|
|
return;
|
|
}
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
int indentPos = -1;
|
|
do {
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
QString blockTextTrimmed = blockText.trimmed();
|
|
if (blockTextTrimmed.size() > 0) {
|
|
QChar firstChar = blockTextTrimmed.at(0);
|
|
QChar lastChar = blockTextTrimmed.at(blockTextTrimmed.size()-1);
|
|
if (lastChar == "{" || (firstChar == "<" && lastChar == ">") || indentPos>=0) {
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
if (indentPos>0 && !curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, indentPos)) {
|
|
indentPos = -1;
|
|
continue;
|
|
}
|
|
QString prefix = "";
|
|
bool isSpace = false;
|
|
while(curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) {
|
|
int pos = curs.positionInBlock();
|
|
QChar prevChar = blockText[pos-1];
|
|
if (prevChar == "\t" && prefix.size()==0) {
|
|
isSpace = false;
|
|
prefix = prevChar;
|
|
break;
|
|
} else if (prevChar == " ") {
|
|
prefix += prevChar;
|
|
isSpace = true;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (indentPos<0) {
|
|
indentPos = prefix.size();
|
|
} else if (prefix.size()>0) {
|
|
if (!isSpace) {
|
|
tabType = "tabs";
|
|
} else {
|
|
tabType = "spaces";
|
|
tabWidth = prefix.size();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
indentPos = -1;
|
|
}
|
|
} while(curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor));
|
|
tabWidthPixels = tabType == "spaces" ? fm.horizontalAdvance(QString(" ").repeated(tabWidth)) : tabStopDistance();
|
|
}
|
|
|
|
void Editor::convertNewLines(QString & txt)
|
|
{
|
|
if (txt.indexOf("\r\n") >= 0) {
|
|
txt.replace("\r\n", "\n");
|
|
} else if (txt.indexOf("\n") < 0 && txt.indexOf("\r") >= 0) {
|
|
txt.replace("\r", "\n");
|
|
}
|
|
}
|
|
|
|
QString Editor::cleanUpText(QString blockText)
|
|
{
|
|
int bStart = -1, bLength = -1, bOffset = 0;
|
|
do {
|
|
QRegularExpressionMatch match = phpExpr.match(blockText, bOffset);
|
|
bStart = match.capturedStart();
|
|
if (bStart>=0) {
|
|
bLength = match.capturedLength();
|
|
QString r(" ");
|
|
blockText = blockText.replace(bStart, bLength, r.repeated(bLength));
|
|
bOffset = bStart + bLength;
|
|
}
|
|
} while (bStart>=0);
|
|
bStart = -1; bLength = -1; bOffset = 0;
|
|
do {
|
|
QRegularExpressionMatch match = strExpr.match(blockText, bOffset);
|
|
bStart = match.capturedStart();
|
|
if (bStart>=0) {
|
|
bLength = match.capturedLength();
|
|
QString r(" ");
|
|
blockText = blockText.replace(bStart, bLength, r.repeated(bLength));
|
|
bOffset = bStart + bLength;
|
|
}
|
|
} while (bStart>=0);
|
|
return blockText;
|
|
}
|
|
|
|
void Editor::showTooltip(int x, int y, QString text, bool richText, int fixedWidth)
|
|
{
|
|
if (!focused) return;
|
|
/* QDesktopWidget::screenGeometry() is deprecated */
|
|
/*
|
|
QRect rec = QApplication::desktop()->screenGeometry();
|
|
int screenWidth = rec.width();
|
|
*/
|
|
QScreen * screen = QGuiApplication::primaryScreen();
|
|
int screenWidth = screen->geometry().width();
|
|
QFontMetrics fm = tooltipLabel.fontMetrics();
|
|
QMargins mm = tooltipLabel.contentsMargins();
|
|
QString strippedText(text.replace("\t", QString(" ").repeated(tabWidth)));
|
|
if (richText) strippedText.replace("<", "<").replace(">", ">").replace("<br />","\n").replace(stripTagsExpr,"").replace(" "," ");
|
|
int textLineWidth = 0;
|
|
QStringList strippedList;
|
|
strippedList = strippedText.split("\n");
|
|
for (int i=0; i<strippedList.size(); i++) {
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
int textLineW = fm.width(strippedList.at(i));
|
|
*/
|
|
int textLineW = fm.horizontalAdvance(strippedList.at(i));
|
|
if (textLineW > textLineWidth) textLineWidth = textLineW;
|
|
}
|
|
int tooltipWidth = textLineWidth + mm.left() + mm.right() + 1;
|
|
if (fixedWidth > 0) tooltipWidth = fixedWidth;
|
|
int tooltipLeft = x;
|
|
QPoint pL(tooltipLeft, 0);
|
|
QPoint pLG = QWidget::mapToGlobal(pL);
|
|
if (pLG.x() + tooltipWidth + TOOLTIP_SCREEN_MARGIN > screenWidth) {
|
|
if (tooltipWidth + 2 * TOOLTIP_SCREEN_MARGIN < screenWidth) {
|
|
pLG.setX(screenWidth - tooltipWidth - TOOLTIP_SCREEN_MARGIN);
|
|
} else {
|
|
pLG.setX(TOOLTIP_SCREEN_MARGIN);
|
|
tooltipWidth = screenWidth - 2 * TOOLTIP_SCREEN_MARGIN;
|
|
}
|
|
}
|
|
if (pLG.x() + tooltipWidth + 10 > screenWidth) {
|
|
tooltipWidth = screenWidth - pLG.x() - 10;
|
|
}
|
|
tooltipLabel.setFixedWidth(tooltipWidth);
|
|
int extraLinesCo = 0;
|
|
for (int i=0; i<strippedList.size(); i++) {
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
int textLineW = fm.width(strippedList.at(i)) + mm.left() + mm.right() + 1;
|
|
*/
|
|
int textLineW = fm.horizontalAdvance(strippedList.at(i)) + mm.left() + mm.right() + 1;
|
|
if (textLineW > tooltipWidth) extraLinesCo += static_cast<int>(std::ceil(static_cast<double>(textLineW) / tooltipWidth))-1;
|
|
}
|
|
tooltipLabel.setFixedHeight(fm.height()*(strippedList.size()+extraLinesCo) + mm.top() + mm.bottom());
|
|
if (richText) tooltipLabel.setTextFormat(Qt::RichText);
|
|
else tooltipLabel.setTextFormat(Qt::PlainText);
|
|
tooltipLabel.setText(text);
|
|
tooltipLabel.show();
|
|
int tooltipHeight = tooltipLabel.geometry().height();
|
|
int tooltipTop = y - tooltipHeight;
|
|
QPoint pR (0, tooltipTop);
|
|
QPoint pRG = QWidget::mapToGlobal(pR);
|
|
if (pRG.y() < 0) pRG.setY(0);
|
|
tooltipLabel.move(QPoint(pLG.x(), pRG.y()));
|
|
}
|
|
|
|
void Editor::showTooltip(QTextCursor * curs, QString text, bool richText, int fixedWidth)
|
|
{
|
|
int viewLeft = viewport()->geometry().left();
|
|
int viewTop = viewport()->geometry().top();
|
|
int cursLeft = cursorRect(* curs).left();
|
|
int cursTop = cursorRect(* curs).top();
|
|
showTooltip(viewLeft + cursLeft - TOOLTIP_OFFSET, viewTop + cursTop - TOOLTIP_OFFSET, text, richText, fixedWidth);
|
|
}
|
|
|
|
void Editor::hideTooltip()
|
|
{
|
|
if (tooltipLabel.isVisible()) tooltipLabel.hide();
|
|
}
|
|
|
|
bool Editor::event(QEvent *e)
|
|
{
|
|
// track mouse
|
|
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
|
|
if (e->type() == QEvent::ToolTip){
|
|
QHelpEvent * helpEvent = static_cast<QHelpEvent *>(e);
|
|
QPoint helpPoint = helpEvent->pos();
|
|
helpPoint.setX(helpPoint.x() - viewport()->geometry().left());
|
|
helpPoint.setY(helpPoint.y() - viewport()->geometry().top());
|
|
QTextCursor curs = cursorForPosition(helpPoint);
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
int pos = curs.positionInBlock();
|
|
if (!highlight->isStateOpen(&block, pos)) {
|
|
QChar prevChar = '\0', nextChar = '\0';
|
|
if (pos > 0 && curs.selectedText().size()==0) prevChar = blockText[pos - 1];
|
|
if (pos < total && curs.selectedText().size()==0) nextChar = blockText[pos];
|
|
// text under cursor
|
|
QString cursorText = "", prevText = "", nextText = "";
|
|
bool hasAlpha = false;
|
|
std::string mode = highlight->findModeAtCursor(&block, pos);
|
|
QChar cursorTextPrevChar = '\0';
|
|
int cursorTextPos = pos;
|
|
if (curs.selectedText().size()==0) {
|
|
for (int i=pos; i>0; i--) {
|
|
QString c = blockText.mid(i-1, 1);
|
|
if (!hasAlpha && isalpha(c[0].toLatin1())) hasAlpha = true;
|
|
cursorTextPrevChar = c[0];
|
|
if (mode == MODE_PHP) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_" || c=="\\") prevText = c + prevText;
|
|
else break;
|
|
} else if (mode == MODE_JS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else {
|
|
if (isalnum(c[0].toLatin1()) || c=="_") prevText = c + prevText;
|
|
else break;
|
|
}
|
|
cursorTextPos = i-1;
|
|
}
|
|
for (int i=pos; i<total; i++) {
|
|
QString c = blockText.mid(i, 1);
|
|
if (!hasAlpha && isalpha(c[0].toLatin1())) hasAlpha = true;
|
|
if (mode == MODE_PHP) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_" || c=="\\") nextText += c;
|
|
else break;
|
|
} else if (mode == MODE_JS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") nextText += c;
|
|
else break;
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") nextText += c;
|
|
else break;
|
|
} else {
|
|
if (isalnum(c[0].toLatin1()) || c=="_") nextText += c;
|
|
else break;
|
|
}
|
|
}
|
|
if (prevText.size()>0 && nextText.size()>0 && hasAlpha) {
|
|
cursorText = prevText + nextText;
|
|
}
|
|
}
|
|
if (cursorText.size() > 0) {
|
|
clearTextHoverFormat();
|
|
if (modifiers & Qt::ControlModifier) {
|
|
// show underline
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, cursorText.size());
|
|
QTextEdit::ExtraSelection selectedWordSelection;
|
|
selectedWordSelection.format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
|
selectedWordSelection.cursor = curs;
|
|
wordsExtraSelections.append(selectedWordSelection);
|
|
highlightExtras();
|
|
// function or class method description
|
|
QString name = "", descName = "", descText = "";
|
|
if (cursorText.size() > 0 && mode == MODE_PHP) {
|
|
name = cursorText;
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QChar prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QChar prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QString nsName = highlight->findNsPHPAtCursor(&block, cursorTextPos);
|
|
QString clsName = highlight->findClsPHPAtCursor(&block, cursorTextPos);
|
|
QString funcName = highlight->findFuncPHPAtCursor(&block, cursorTextPos);
|
|
if ((prevChar == ">" && prevPrevChar == "-") || (prevChar == ":" && prevPrevChar == ":")) {
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QString prevType = detectCompleteTypeAtCursorPHP(curs, nsName, clsName, funcName);
|
|
if (prevType.size() > 0 && prevType.at(0) == "\\") prevType = prevType.mid(1);
|
|
if (prevType.size() > 0) {
|
|
name = prevType + "::" + cursorText;
|
|
HPW->phpClassMethodDescsIterator = HPW->phpClassMethodDescs.find(name.toStdString());
|
|
if (HPW->phpClassMethodDescsIterator != HPW->phpClassMethodDescs.end()) {
|
|
descName = QString::fromStdString(HPW->phpClassMethodDescsIterator->first);
|
|
descText = QString::fromStdString(HPW->phpClassMethodDescsIterator->second);
|
|
descText.replace("<", "<").replace(">", ">");
|
|
}
|
|
}
|
|
} else {
|
|
HPW->phpFunctionDescsIterator = HPW->phpFunctionDescs.find(name.toLower().toStdString());
|
|
if (HPW->phpFunctionDescsIterator != HPW->phpFunctionDescs.end()) {
|
|
descName = QString::fromStdString(HPW->phpFunctionDescsIterator->first);
|
|
descText = QString::fromStdString(HPW->phpFunctionDescsIterator->second);
|
|
descText.replace("<", "<").replace(">", ">");
|
|
}
|
|
if (descName.size() == 0 || descText.size() == 0) {
|
|
name = completeClassNamePHPAtCursor(curs, name, nsName);
|
|
}
|
|
}
|
|
if (name.size() > 0) {
|
|
CW->tooltipsIteratorPHP = CW->tooltipsPHP.find(name.toStdString());
|
|
if (CW->tooltipsIteratorPHP != CW->tooltipsPHP.end()) {
|
|
QString fName = QString::fromStdString(CW->tooltipsIteratorPHP->first);
|
|
QString params = QString::fromStdString(CW->tooltipsIteratorPHP->second);
|
|
if (params.indexOf(TOOLTIP_DELIMITER) >= 0) {
|
|
params.replace(TOOLTIP_DELIMITER, "<br />" + fName);
|
|
}
|
|
if (fName.indexOf("::")>0) fName = getFixedCompleteClassMethodName(fName, params);
|
|
descName = fName + " " + params;
|
|
}
|
|
}
|
|
}
|
|
if (descName.size() > 0) {
|
|
QString tooltipText = descName;
|
|
if (descText.size() > 0) tooltipText += "<br /><br />" + descText.replace("\n", "<br />");
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
showTooltip(& curs, tooltipText);
|
|
}
|
|
} else {
|
|
// function definition
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QChar prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QChar prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
if (mode == MODE_PHP) {
|
|
bool showAllowed = false;
|
|
if ((prevChar != ">" || prevPrevChar != "-") && (prevChar != ":" || prevPrevChar != ":")) showAllowed = true;
|
|
CW->tooltipsIteratorPHP = CW->tooltipsPHP.find(cursorText.toStdString());
|
|
if (showAllowed && CW->tooltipsIteratorPHP != CW->tooltipsPHP.end()) {
|
|
QString fName = QString::fromStdString(CW->tooltipsIteratorPHP->first);
|
|
QString params = QString::fromStdString(CW->tooltipsIteratorPHP->second);
|
|
if (params.indexOf(TOOLTIP_DELIMITER) >= 0) {
|
|
params.replace(TOOLTIP_DELIMITER, "<br />" + fName);
|
|
}
|
|
QString tooltipText = fName + " " + params;
|
|
showTooltip(& curs, tooltipText);
|
|
}
|
|
} else {
|
|
QRegularExpressionMatch m = colorExpr.match(cursorText);
|
|
if (m.capturedStart()==0 && QColor::isValidColor(cursorText)) {
|
|
showTooltip(& curs, TOOLTIP_COLOR_TPL.arg(cursorText)+" "+cursorText);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
clearTextHoverFormat();
|
|
if (prevText.size() > 0 && nextText.size() > 0) {
|
|
QString nonAlphaText = prevText+nextText;
|
|
QRegularExpressionMatch m = colorExpr.match(nonAlphaText);
|
|
if (m.capturedStart()==0 && QColor::isValidColor(nonAlphaText)) {
|
|
showTooltip(& curs, TOOLTIP_COLOR_TPL.arg(nonAlphaText)+" "+nonAlphaText);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return QTextEdit::event(e);
|
|
}
|
|
|
|
void Editor::clearTextHoverFormat()
|
|
{
|
|
if (wordsExtraSelections.size() > 0) {
|
|
wordsExtraSelections.clear();
|
|
}
|
|
}
|
|
|
|
void Editor::clearErrorsFormat()
|
|
{
|
|
if (errorsExtraSelections.size() > 0) {
|
|
errorsExtraSelections.clear();
|
|
highlightExtras();
|
|
}
|
|
}
|
|
|
|
void Editor::highlightError(int pos, int length)
|
|
{
|
|
if (pos < 0) return;
|
|
// show wave underline
|
|
QTextCursor cursor = textCursor();
|
|
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
|
if (pos > cursor.position()) return;
|
|
cursor.setPosition(pos);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, length);
|
|
QTextEdit::ExtraSelection errorSelection;
|
|
errorSelection.format.setUnderlineColor(lineErrorColor);
|
|
errorSelection.format.setUnderlineStyle(QTextCharFormat::WaveUnderline);
|
|
errorSelection.cursor = cursor;
|
|
errorsExtraSelections.append(errorSelection);
|
|
highlightExtras();
|
|
}
|
|
|
|
void Editor::highlightErrorLine(int line)
|
|
{
|
|
if (line < 1) return;
|
|
// show wave underline
|
|
QTextCursor cursor = textCursor();
|
|
cursor.movePosition(QTextCursor::Start);
|
|
if (line > 1) cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, line-1);
|
|
QString blockText = cursor.block().text();
|
|
do {
|
|
int pos = cursor.positionInBlock();
|
|
if (pos >= blockText.size()) break;
|
|
QChar c = blockText[pos];
|
|
if (!c.isSpace()) break;
|
|
} while(cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor));
|
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
QTextEdit::ExtraSelection errorSelection;
|
|
errorSelection.format.setUnderlineColor(lineErrorColor);
|
|
errorSelection.format.setUnderlineStyle(QTextCharFormat::WaveUnderline);
|
|
errorSelection.cursor = cursor;
|
|
errorsExtraSelections.append(errorSelection);
|
|
highlightExtras();
|
|
}
|
|
|
|
void Editor::suggestWords(QStringList words, int cursorTextPos)
|
|
{
|
|
completePopup->clearItems();
|
|
for (QString word : words) {
|
|
completePopup->addItem(word, word);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
if (completePopup->count()>0) {
|
|
completePopup->setTextStartPos(cursorTextPos);
|
|
showCompletePopup();
|
|
}
|
|
}
|
|
|
|
void Editor::updateSizes(bool updateGeometry)
|
|
{
|
|
updateViewportMargins();
|
|
if (updateGeometry) updateWidgetsGeometry();
|
|
}
|
|
|
|
void Editor::resizeEvent(QResizeEvent * e)
|
|
{
|
|
QTextEdit::resizeEvent(e);
|
|
firstVisibleBlockIndex = -1;
|
|
lastVisibleBlockIndex = -1;
|
|
hidePopups();
|
|
updateWidgetsGeometry();
|
|
updateLineAnnotationView();
|
|
if (!searchDisplayOnTop && static_cast<Search *>(search)->isVisible()) {
|
|
static_cast<Search *>(search)->updateScrollBar();
|
|
}
|
|
}
|
|
|
|
void Editor::updateWidgetsGeometry()
|
|
{
|
|
QRect cr = contentsRect();
|
|
int lineW = lineNumberAreaWidth();
|
|
int markW = lineMarkAreaWidth();
|
|
int mapW = lineMapAreaWidth();
|
|
int searchH = searchWidgetHeight();
|
|
if (!search->isVisible()) searchH = 0;
|
|
int breadcrumbsH = breadcrumbsHeight();
|
|
|
|
int vScrollW = verticalScrollBar()->geometry().width();
|
|
int hScrollH = horizontalScrollBar()->geometry().height();
|
|
if (verticalScrollBar()->maximum() <= verticalScrollBar()->minimum()) vScrollW = 0;
|
|
if (horizontalScrollBar()->maximum() <= horizontalScrollBar()->minimum() || searchH == 0) hScrollH = 0;
|
|
|
|
lineMap->setGeometry(QRect(cr.right()-mapW-vScrollW+1, cr.top(), mapW+vScrollW, cr.height()));
|
|
breadcrumbs->setGeometry(QRect(cr.left(), cr.top(), cr.width()-mapW-vScrollW, breadcrumbsH));
|
|
qaBtn->setGeometry(0, 0, lineW, breadcrumbsH-1);
|
|
if (!searchDisplayOnTop) {
|
|
search->setGeometry(QRect(cr.left(), cr.bottom()-searchH-hScrollH+1, cr.width()-mapW-vScrollW, searchH+hScrollH));
|
|
lineNumber->setGeometry(QRect(cr.left(), cr.top()+breadcrumbsH, lineW, cr.height()-breadcrumbsH-searchH-hScrollH));
|
|
lineMark->setGeometry(QRect(cr.left()+lineW, cr.top()+breadcrumbsH, markW, cr.height()-breadcrumbsH-searchH-hScrollH));
|
|
QMargins searchMargins = search->contentsMargins();
|
|
searchMargins.setBottom(hScrollH);
|
|
search->setContentsMargins(searchMargins);
|
|
} else {
|
|
search->setGeometry(QRect(cr.left(), cr.top()+breadcrumbsH, cr.width()-mapW-vScrollW, searchH));
|
|
lineNumber->setGeometry(QRect(cr.left(), cr.top()+breadcrumbsH+searchH, lineW, cr.height()-breadcrumbsH-searchH));
|
|
lineMark->setGeometry(QRect(cr.left()+lineW, cr.top()+breadcrumbsH+searchH, markW, cr.height()-breadcrumbsH-searchH));
|
|
}
|
|
}
|
|
|
|
void Editor::backtab()
|
|
{
|
|
if (!focused) return;
|
|
QTextCursor curs = textCursor();
|
|
if (curs.selectedText().size()==0) {
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int pos = curs.positionInBlock();
|
|
int count = 0;
|
|
if (pos > 0) {
|
|
QChar prevChar = blockText[pos-1];
|
|
if (prevChar == "\t" && curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor)) {
|
|
count++;
|
|
}
|
|
}
|
|
if (count==0) {
|
|
while (pos > 0) {
|
|
QChar prevChar = blockText[pos-1];
|
|
if (prevChar == " " && curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor)) {
|
|
count++;
|
|
pos = curs.positionInBlock();
|
|
if (count >= tabWidth) break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (count>0) {
|
|
curs.deleteChar();
|
|
}
|
|
}
|
|
if (curs.selectedText().size()>0) {
|
|
bool changed = false;
|
|
int start = curs.selectionStart();
|
|
int end = curs.selectionEnd();
|
|
curs.setPosition(end);
|
|
int endBlockNumber = curs.block().blockNumber();
|
|
curs.setPosition(start);
|
|
do {
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
int pos = curs.positionInBlock();
|
|
int count = 0;
|
|
if (pos < total) {
|
|
QChar nextChar = blockText[pos];
|
|
if (nextChar == "\t" && curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor)) {
|
|
count++;
|
|
}
|
|
}
|
|
if (count == 0) {
|
|
while (pos < total) {
|
|
QChar nextChar = blockText[pos];
|
|
if (nextChar == " " && curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor)) {
|
|
count++;
|
|
pos = curs.positionInBlock();
|
|
if (count >= tabWidth) break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (count>0) {
|
|
if (!changed) {
|
|
curs.beginEditBlock();
|
|
changed = true;
|
|
}
|
|
curs.deleteChar();
|
|
}
|
|
if (curs.block().blockNumber()>=endBlockNumber) break;
|
|
} while(curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor));
|
|
if (changed) {
|
|
curs.endEditBlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::duplicateLine()
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
if (curs.hasSelection()) return;
|
|
QString text = curs.block().text();
|
|
curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
curs.insertText("\n"+text);
|
|
|
|
// spell check
|
|
if (textCursor().blockNumber() != curs.blockNumber() && spellCheckerEnabled && spellChecker != nullptr && !isBigFile) {
|
|
spellPastedBlocksQueue.append(curs.block().blockNumber());
|
|
QTimer::singleShot(INTERVAL_SPELL_CHECK_MILLISECONDS, this, SLOT(spellCheckPasted()));
|
|
}
|
|
}
|
|
|
|
void Editor::deleteLine()
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
if (curs.hasSelection()) return;
|
|
curs.beginEditBlock();
|
|
if (curs.block().text().size() > 0) {
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
curs.removeSelectedText();
|
|
}
|
|
if (curs.block().blockNumber() < document()->blockCount()-1) {
|
|
curs.deleteChar();
|
|
} else if (curs.block().blockNumber() > 0) {
|
|
curs.deletePreviousChar();
|
|
}
|
|
curs.endEditBlock();
|
|
}
|
|
|
|
void Editor::comment()
|
|
{
|
|
if (!focused) return;
|
|
QTextCursor curs = textCursor();
|
|
int selectedSize = curs.selectedText().size();
|
|
if (selectedSize==0) {
|
|
curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
if (blockText.trimmed().size() == 0) return;
|
|
int pos = curs.positionInBlock();
|
|
int total = blockText.size();
|
|
std::string mode = highlight->findModeAtCursor(&block, pos);
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
block = curs.block();
|
|
blockText = block.text();
|
|
pos = curs.positionInBlock();
|
|
total = blockText.size();
|
|
if (highlight->findModeAtCursor(&block, pos) != mode) return;
|
|
if (mode == MODE_PHP || mode == MODE_JS) {
|
|
if (blockText.trimmed().indexOf("//") == 0) {
|
|
if (blockText.indexOf("/") > 0) curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, blockText.indexOf("/"));
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
|
|
curs.deleteChar();
|
|
} else {
|
|
curs.insertText("//");
|
|
}
|
|
} else if (mode == MODE_CSS) {
|
|
if (pos+1 < total && blockText[pos] == "/" && blockText[pos+1] == "*" && pos+1 < total-2 && blockText[total-2] == "*" && blockText[total-1] == "/") {
|
|
curs.beginEditBlock();
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
|
|
curs.deleteChar();
|
|
curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, 2);
|
|
curs.deleteChar();
|
|
curs.endEditBlock();
|
|
} else {
|
|
curs.beginEditBlock();
|
|
curs.insertText("/*");
|
|
curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
curs.insertText("*/");
|
|
curs.endEditBlock();
|
|
}
|
|
} else if (mode == MODE_HTML) {
|
|
if (pos+3 < total && blockText[pos] == "<" && blockText[pos+1] == "!" && blockText[pos+2] == "-" && blockText[pos+3] == "-" &&
|
|
pos+3 < total-3 && blockText[total-3] == "-" && blockText[total-2] == "-" && blockText[total-1] == ">"
|
|
) {
|
|
curs.beginEditBlock();
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
|
|
curs.deleteChar();
|
|
curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, 3);
|
|
curs.deleteChar();
|
|
curs.endEditBlock();
|
|
} else {
|
|
curs.beginEditBlock();
|
|
curs.insertText("<!--");
|
|
curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
curs.insertText("-->");
|
|
curs.endEditBlock();
|
|
}
|
|
}
|
|
}
|
|
if (selectedSize>0) {
|
|
int start = curs.selectionStart();
|
|
int end = curs.selectionEnd();
|
|
curs.setPosition(end);
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int pos = curs.positionInBlock();
|
|
int total = blockText.size();
|
|
std::string mode = highlight->findModeAtCursor(&block, pos);
|
|
int endBlockNumber = block.blockNumber();
|
|
curs.setPosition(start);
|
|
block = curs.block();
|
|
blockText = block.text();
|
|
pos = curs.positionInBlock();
|
|
total = blockText.size();
|
|
if (highlight->findModeAtCursor(&block, pos) != mode) return;
|
|
int moveEnd = end;
|
|
curs.beginEditBlock();
|
|
if (mode == MODE_PHP || mode == MODE_JS) {
|
|
do {
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
block = curs.block();
|
|
blockText = block.text();
|
|
if (blockText.trimmed().size() == 0) continue;
|
|
pos = curs.positionInBlock();
|
|
total = blockText.size();
|
|
if (highlight->findModeAtCursor(&block, pos) != mode) break;
|
|
if (blockText.trimmed().indexOf("//") == 0) {
|
|
if (blockText.indexOf("/") > 0) curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, blockText.indexOf("/"));
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
|
|
curs.deleteChar();
|
|
moveEnd -= 2;
|
|
} else {
|
|
curs.insertText("//");
|
|
moveEnd += 2;
|
|
}
|
|
if (curs.block().blockNumber()>=endBlockNumber) break;
|
|
} while(curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor));
|
|
} else if (mode == MODE_CSS) {
|
|
bool hasStart = false, hasEnd = false;
|
|
curs.setPosition(start);
|
|
block = curs.block();
|
|
blockText = block.text();
|
|
pos = curs.positionInBlock();
|
|
total = blockText.size();
|
|
if (pos+1 < total && blockText[pos] == "/" && blockText[pos+1] == "*") {
|
|
hasStart = true;
|
|
}
|
|
curs.setPosition(end);
|
|
block = curs.block();
|
|
blockText = block.text();
|
|
pos = curs.positionInBlock();
|
|
total = blockText.size();
|
|
if (selectedSize >= 4 && pos>1 && blockText[pos-2] == "*" && blockText[pos-1] == "/") {
|
|
hasEnd = true;
|
|
}
|
|
if (hasStart && hasEnd) {
|
|
curs.setPosition(start);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
|
|
curs.deleteChar();
|
|
curs.setPosition(end - 2);
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, 2);
|
|
curs.deleteChar();
|
|
moveEnd -= 4;
|
|
} else {
|
|
curs.setPosition(start);
|
|
curs.insertText("/*");
|
|
curs.setPosition(end + 2);
|
|
curs.insertText("*/");
|
|
moveEnd += 4;
|
|
}
|
|
} else if (mode == MODE_HTML) {
|
|
bool hasStart = false, hasEnd = false;
|
|
curs.setPosition(start);
|
|
block = curs.block();
|
|
blockText = block.text();
|
|
pos = curs.positionInBlock();
|
|
total = blockText.size();
|
|
if (pos+3 < total && blockText[pos] == "<" && blockText[pos+1] == "!" && blockText[pos+2] == "-" && blockText[pos+3] == "-") {
|
|
hasStart = true;
|
|
}
|
|
curs.setPosition(end);
|
|
block = curs.block();
|
|
blockText = block.text();
|
|
pos = curs.positionInBlock();
|
|
total = blockText.size();
|
|
if (selectedSize >= 7 && pos>2 && blockText[pos-3] == "-" && blockText[pos-2] == "-" && blockText[pos-1] == ">") {
|
|
hasEnd = true;
|
|
}
|
|
if (hasStart && hasEnd) {
|
|
curs.setPosition(start);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
|
|
curs.deleteChar();
|
|
curs.setPosition(end - 4);
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, 3);
|
|
curs.deleteChar();
|
|
moveEnd -= 7;
|
|
} else {
|
|
curs.setPosition(start);
|
|
curs.insertText("<!--");
|
|
curs.setPosition(end + 4);
|
|
curs.insertText("-->");
|
|
moveEnd += 7;
|
|
}
|
|
}
|
|
curs.endEditBlock();
|
|
if (moveEnd != end) {
|
|
curs.setPosition(start);
|
|
curs.setPosition(moveEnd, QTextCursor::KeepAnchor);
|
|
setTextCursor(curs);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::focusInEvent(QFocusEvent *e)
|
|
{
|
|
focused = true;
|
|
if (fileName.size() > 0 && !Helper::fileExists(fileName) && !warningDisplayed) {
|
|
setFileIsDeleted();
|
|
} else if (fileName.size() > 0 && Helper::fileExists(fileName) && lastModifiedMsec > 0 && !warningDisplayed) {
|
|
QFileInfo fInfo(fileName);
|
|
QDateTime dtModified = fInfo.lastModified();
|
|
if (dtModified.time().msec() != lastModifiedMsec) {
|
|
setFileIsOutdated();
|
|
}
|
|
}
|
|
if (!searchDisplayOnTop && static_cast<Search *>(search)->isVisible()) {
|
|
static_cast<Search *>(search)->updateScrollBar();
|
|
}
|
|
QTextEdit::focusInEvent(e);
|
|
emit focusIn(tabIndex);
|
|
}
|
|
|
|
void Editor::focusOutEvent(QFocusEvent *e)
|
|
{
|
|
focused = false;
|
|
hidePopups();
|
|
QTextEdit::focusOutEvent(e);
|
|
}
|
|
|
|
void Editor::setFileIsDeleted()
|
|
{
|
|
if (warningDisplayed) return;
|
|
warningDisplayed = true;
|
|
if (modified) emit modifiedStateChanged(tabIndex, false);
|
|
modified = true;
|
|
emit warning(tabIndex, "deleted", QObject::tr("File \"%1\" not found.").arg(fileName));
|
|
emit modifiedStateChanged(tabIndex, modified);
|
|
}
|
|
|
|
void Editor::setFileIsOutdated()
|
|
{
|
|
if (warningDisplayed) return;
|
|
warningDisplayed = true;
|
|
if (modified) emit modifiedStateChanged(tabIndex, false);
|
|
modified = true;
|
|
emit warning(tabIndex, "outdated", QObject::tr("File \"%1\" was modified externally.").arg(fileName));
|
|
emit modifiedStateChanged(tabIndex, modified);
|
|
}
|
|
|
|
bool Editor::onKeyPress(QKeyEvent *e)
|
|
{
|
|
bool shift = false, ctrl = false;
|
|
if (e->modifiers() & Qt::ShiftModifier) shift = true;
|
|
if (e->modifiers() & Qt::ControlModifier) ctrl = true;
|
|
int code = e->key();
|
|
lastKeyPressed = code;
|
|
lastKeyPressedBlockNumber = textCursor().block().blockNumber();
|
|
// shift + enter will add new row to existing block
|
|
if (code == Qt::Key_Return && shift) return false;
|
|
// multiselect
|
|
if (isMultiSelectMode) {
|
|
QString text = e->text();
|
|
if (text.size() > 0 && !ctrl && code != Qt::Key_Return && code != Qt::Key_Backspace && code != Qt::Key_Delete && code != Qt::Key_Escape) {
|
|
multiSelectCursor.joinPreviousEditBlock();
|
|
multiSelectCursor.insertText(text);
|
|
multiSelectInsertText += text;
|
|
for (QTextCursor cursor : multiSelectCursors) {
|
|
cursor.insertText(text);
|
|
// modified state
|
|
modifiedLinesIterator = modifiedLines.find(cursor.block().blockNumber() + 1);
|
|
if (modifiedLinesIterator == modifiedLines.end()) {
|
|
modifiedLines[cursor.block().blockNumber() + 1] = cursor.block().blockNumber() + 1;
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData != nullptr) {
|
|
blockData->isModified = true;
|
|
cursor.block().setUserData(blockData);
|
|
}
|
|
}
|
|
}
|
|
multiSelectCursor.endEditBlock();
|
|
lineNumber->update();
|
|
} else if (code == Qt::Key_Backspace && (multiSelectInsertText.size() > 0 || multiSelectCursor.hasSelection())) {
|
|
if (!multiSelectCursor.hasSelection() && multiSelectCursor.positionInBlock() > 0) {
|
|
multiSelectCursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
|
}
|
|
multiSelectCursor.joinPreviousEditBlock();
|
|
if (multiSelectCursor.hasSelection()) {
|
|
if (multiSelectCursor.selectedText().size() <= multiSelectInsertText.size()) {
|
|
multiSelectInsertText = multiSelectInsertText.remove(multiSelectInsertText.size()-multiSelectCursor.selectedText().size(), multiSelectCursor.selectedText().size());
|
|
}
|
|
multiSelectCursor.deleteChar();
|
|
}
|
|
for (QTextCursor cursor : multiSelectCursors) {
|
|
if (!cursor.hasSelection() && cursor.positionInBlock() > 0) {
|
|
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
|
}
|
|
if (cursor.hasSelection()) {
|
|
cursor.deleteChar();
|
|
// modified state
|
|
modifiedLinesIterator = modifiedLines.find(cursor.block().blockNumber() + 1);
|
|
if (modifiedLinesIterator == modifiedLines.end()) {
|
|
modifiedLines[cursor.block().blockNumber() + 1] = cursor.block().blockNumber() + 1;
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData != nullptr) {
|
|
blockData->isModified = true;
|
|
cursor.block().setUserData(blockData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
multiSelectCursor.endEditBlock();
|
|
lineNumber->update();
|
|
} else if (!shift && !ctrl && code != Qt::Key_Backspace) {
|
|
multiSelectToggle();
|
|
}
|
|
setTextCursor(multiSelectCursor);
|
|
|
|
QList<QTextEdit::ExtraSelection> extraSelections;
|
|
highlightCurrentLine(& extraSelections);
|
|
highlightMultiSelection(& extraSelections);
|
|
setExtraSelections(extraSelections);
|
|
|
|
return false;
|
|
}
|
|
bool ignoreKey = false; // workaround for wrong key code, android emulator bug ?
|
|
#if defined(Q_OS_ANDROID)
|
|
/*
|
|
if (code == Qt::Key_BracketLeft && e->text() != "[") ignoreKey = true;
|
|
if (code == Qt::Key_ParenLeft && e->text() != "(") ignoreKey = true;
|
|
if (code == Qt::Key_BraceLeft && e->text() != "{") ignoreKey = true;
|
|
if (code == Qt::Key_BracketRight && e->text() != "]") ignoreKey = true;
|
|
if (code == Qt::Key_ParenRight && e->text() != ")") ignoreKey = true;
|
|
if (code == Qt::Key_BraceRight && e->text() != "}") ignoreKey = true;
|
|
if (code == Qt::Key_Apostrophe && e->text() != "'") ignoreKey = true;
|
|
if (code == Qt::Key_QuoteDbl && e->text() != "\"") ignoreKey = true;
|
|
*/
|
|
if (e->text() == "[") code = Qt::Key_BracketLeft;
|
|
if (e->text() == "(") code = Qt::Key_ParenLeft;
|
|
if (e->text() == "{") code = Qt::Key_BraceLeft;
|
|
if (e->text() == "]") code = Qt::Key_BracketRight;
|
|
if (e->text() == ")") code = Qt::Key_ParenRight;
|
|
if (e->text() == "}") code = Qt::Key_BraceRight;
|
|
if (e->text() == ">") code = Qt::Key_Greater;
|
|
if (e->text() == "<") code = Qt::Key_Less;
|
|
if (e->text() == "'") code = Qt::Key_Apostrophe;
|
|
if (e->text() == "\"") code = Qt::Key_QuoteDbl;
|
|
#endif
|
|
// insert quotes & brackets pair
|
|
if ((code == Qt::Key_QuoteDbl || code == Qt::Key_Apostrophe || code == Qt::Key_BraceLeft || code == Qt::Key_BracketLeft || code == Qt::Key_ParenLeft) && !ctrl && !ignoreKey) {
|
|
QTextCursor curs = textCursor();
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
int pos = curs.positionInBlock();
|
|
QChar nextChar = '\0';
|
|
if (pos < total) nextChar = blockText[pos];
|
|
bool doInsert = true;
|
|
QString selectedText = curs.selectedText();
|
|
if (selectedText.size() == 0) {
|
|
if ((code == Qt::Key_QuoteDbl && blockText.count("\"")%2!=0) ||
|
|
(code == Qt::Key_Apostrophe && blockText.count("'")%2!=0) ||
|
|
(code == Qt::Key_BraceLeft && blockText.count("{")!=blockText.count("}")) ||
|
|
(code == Qt::Key_BracketLeft && blockText.count("[")!=blockText.count("]")) ||
|
|
(code == Qt::Key_ParenLeft && blockText.count("(")!=blockText.count(")"))
|
|
) {
|
|
doInsert = false;
|
|
}
|
|
if (nextChar == '\0') doInsert = true;
|
|
else if (code != Qt::Key_QuoteDbl && code != Qt::Key_Apostrophe && nextChar != ")" && nextChar != "}" && nextChar != "]" && !iswspace(nextChar.toLatin1())) doInsert = false;
|
|
else if (isalpha(nextChar.toLatin1()) || nextChar == "$") doInsert = false;
|
|
if (doInsert && highlight->isStateOpen(&block ,pos)) doInsert = false;
|
|
} else {
|
|
QString prefix = "";
|
|
QTextCursor cursor = textCursor();
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
int pos = cursor.positionInBlock();
|
|
QString blockText = cursor.block().text();
|
|
int total = blockText.size();
|
|
while(pos < total) {
|
|
if (!cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) break;
|
|
pos = cursor.positionInBlock();
|
|
QChar prevChar = blockText[pos-1];
|
|
if ((prevChar == " " || prevChar == "\t")) {
|
|
prefix += prevChar;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
QString indent = (tabType == "spaces") ? QString(" ").repeated(tabWidth) : "\t";
|
|
selectedText.replace(QString::fromWCharArray(L"\u2029"),"\n");
|
|
if (selectedText.indexOf("\n") >= 0 && code != Qt::Key_QuoteDbl && code != Qt::Key_Apostrophe) {
|
|
selectedText.replace("\n","\n"+indent);
|
|
selectedText = "\n"+prefix+indent+selectedText+"\n"+prefix;
|
|
}
|
|
}
|
|
if (doInsert) {
|
|
QString insert = "";
|
|
if (code == Qt::Key_QuoteDbl) insert = "\""+selectedText+"\"";
|
|
if (code == Qt::Key_Apostrophe) insert = "'"+selectedText+"'";
|
|
if (code == Qt::Key_BraceLeft) insert = "{"+selectedText+"}";
|
|
if (code == Qt::Key_BracketLeft) insert = "["+selectedText+"]";
|
|
if (code == Qt::Key_ParenLeft) insert = "("+selectedText+")";
|
|
if (insert.size()>0) {
|
|
curs.insertText(insert);
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor);
|
|
if (selectedText.size() > 0) {
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, selectedText.size());
|
|
}
|
|
setTextCursor(curs);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// skip & move cursor to next char
|
|
if ((code == Qt::Key_QuoteDbl || code == Qt::Key_Apostrophe || code == Qt::Key_BraceRight || code == Qt::Key_BracketRight || code == Qt::Key_ParenRight) && !ctrl && !ignoreKey) {
|
|
QTextCursor curs = textCursor();
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
int pos = curs.positionInBlock();
|
|
QChar nextChar = '\0';
|
|
if (pos < total) nextChar = blockText[pos];
|
|
if ((code == Qt::Key_QuoteDbl && nextChar == "\"" && blockText.count("\"")%2==0) ||
|
|
(code == Qt::Key_Apostrophe && nextChar == "'" && blockText.count("'")%2==0) ||
|
|
(code == Qt::Key_BraceRight && nextChar == "}" && blockText.count("{")==blockText.count("}")) ||
|
|
(code == Qt::Key_BracketRight && nextChar == "]" && blockText.count("[")==blockText.count("]")) ||
|
|
(code == Qt::Key_ParenRight && nextChar == ")" && blockText.count("(")==blockText.count(")"))
|
|
) {
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
|
|
setTextCursor(curs);
|
|
return false;
|
|
}
|
|
}
|
|
// indent
|
|
if (code == Qt::Key_Tab && !shift && !ctrl) {
|
|
if (search->isVisible() && static_cast<Search *>(search)->isFocused()) {
|
|
return false;
|
|
}
|
|
QTextCursor curs = textCursor();
|
|
if (curs.selectedText().size()==0 && tabType == "spaces") {
|
|
QString insert = " ";
|
|
curs.insertText(insert.repeated(tabWidth));
|
|
return false;
|
|
}
|
|
if (curs.selectedText().size()>0) {
|
|
QString indent = (tabType == "spaces") ? QString(" ").repeated(tabWidth) : "\t";
|
|
curs.beginEditBlock();
|
|
int start = curs.selectionStart();
|
|
int end = curs.selectionEnd();
|
|
curs.setPosition(end);
|
|
int endBlockNumber = curs.block().blockNumber();
|
|
curs.setPosition(start);
|
|
do {
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
curs.insertText(indent);
|
|
if (curs.block().blockNumber()>=endBlockNumber) break;
|
|
} while(curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor));
|
|
curs.endEditBlock();
|
|
return false;
|
|
}
|
|
}
|
|
// remove spaces, quotes, brackets
|
|
if (code == Qt::Key_Backspace && !shift && !ctrl) {
|
|
QTextCursor curs = textCursor();
|
|
if (curs.selectedText().size()==0) {
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int pos = curs.positionInBlock();
|
|
int count = 0;
|
|
// quotes & brackets
|
|
QChar pc = '\0', nc = '\0';
|
|
if (pos > 0) pc = blockText[pos - 1];
|
|
if (pos < blockText.size()) nc = blockText[pos];
|
|
if ((pc == Qt::Key_QuoteDbl && nc == Qt::Key_QuoteDbl) ||
|
|
(pc == Qt::Key_Apostrophe && nc == Qt::Key_Apostrophe) ||
|
|
(pc == Qt::Key_BraceLeft && nc == Qt::Key_BraceRight) ||
|
|
(pc == Qt::Key_BracketLeft && nc == Qt::Key_BracketRight) ||
|
|
(pc == Qt::Key_ParenLeft && nc == Qt::Key_ParenRight)
|
|
) {
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, 2);
|
|
count += 2;
|
|
}
|
|
// spaces
|
|
if (count == 0) {
|
|
while (pos > 0) {
|
|
QChar prevChar = blockText[pos-1];
|
|
if (prevChar == " " && curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor)) {
|
|
count++;
|
|
pos = curs.positionInBlock();
|
|
if (count >= tabWidth) break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (count>0) {
|
|
curs.deleteChar();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// complete, indent and comment on enter
|
|
if (code == Qt::Key_Return && !shift && !ctrl) {
|
|
if (search->isVisible() && static_cast<Search *>(search)->isFocused()) {
|
|
return false;
|
|
}
|
|
// complete
|
|
if (completePopup->isVisible() && completePopup->count()>0) {
|
|
completePopup->chooseCurrentItem();
|
|
return false;
|
|
}
|
|
QTextCursor curs = textCursor();
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
QString blockTextTrimmed = blockText.trimmed();
|
|
int total = blockText.size();
|
|
int pos = curs.positionInBlock();
|
|
int state = highlight->findStateAtCursor(&block, pos);
|
|
bool isCommentOpen = false, isCommentLine = false, isCommentClose = false, isPHPDoc = false;
|
|
if (pos > 1 && blockText[pos-2] == "/" && blockText[pos-1] == "*") {
|
|
isCommentOpen = true;
|
|
} else if (pos > 2 && blockText[pos-3] == "/" && blockText[pos-2] == "*" && blockText[pos-1] == "*") {
|
|
isCommentOpen = true;
|
|
isPHPDoc = true;
|
|
} else if (pos > 2 && blockText[pos-3] == " " && blockText[pos-2] == "*" && blockText[pos-1] == " ") {
|
|
isCommentLine = true;
|
|
} else if (blockText.indexOf(QRegularExpression("^[\\s]*[ ][*][ ]")) >= 0) {
|
|
isCommentLine = true;
|
|
} else if (pos > 2 && blockText[pos-3] == " " && blockText[pos-2] == "*" && blockText[pos-1] == "/") {
|
|
isCommentClose = true;
|
|
}
|
|
QString prefix = "";
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
pos = curs.positionInBlock();
|
|
while(pos < total) {
|
|
if (!curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) break;
|
|
pos = curs.positionInBlock();
|
|
QChar prevChar = blockText[pos-1];
|
|
if ((prevChar == " " || prevChar == "\t")) {
|
|
prefix += prevChar;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if ((isCommentClose || isCommentLine) && prefix.size()>0) prefix = prefix.mid(0, prefix.size()-1);
|
|
if (isCommentOpen && state != STATE_COMMENT_ML_PHP && state != STATE_COMMENT_ML_JS && state != STATE_COMMENT_ML_CSS) isCommentOpen = false;
|
|
if (isCommentLine && state != STATE_COMMENT_ML_PHP && state != STATE_COMMENT_ML_JS && state != STATE_COMMENT_ML_CSS) isCommentLine = false;
|
|
// comment
|
|
if (isCommentOpen || isCommentLine) {
|
|
QTextCursor cursor = textCursor();
|
|
QTextBlock block = cursor.block();
|
|
QVector<QString> phpDocParams;
|
|
QVector<QString> phpDocParamTypes;
|
|
QString phpDocFuncName = "", phpDocReturnVal = "";
|
|
std::string mode = highlight->findModeAtCursor(& block, cursor.positionInBlock());
|
|
if (mode == MODE_PHP || mode == MODE_JS || mode == MODE_CSS) {
|
|
if (isPHPDoc && mode == MODE_PHP) {
|
|
QTextCursor cursorP = textCursor();
|
|
while (cursorP.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor)) {
|
|
QString blockPtext = cursorP.block().text().trimmed();
|
|
if (blockPtext.size()==0) continue;
|
|
QRegularExpressionMatch matchP = functionExpr.match(blockPtext);
|
|
if (matchP.capturedStart()>=0) {
|
|
phpDocFuncName = matchP.captured(1);
|
|
phpDocReturnVal = matchP.captured(3).replace(QRegularExpression("[\\?][\\s]+"), "?");
|
|
QString params = matchP.captured(2);
|
|
params = params.trimmed();
|
|
if (params.size()==0) break;
|
|
QRegularExpressionMatch funcMatch = functionWordExpr.match(params);
|
|
if (funcMatch.capturedStart() >= 0) break;
|
|
QStringList paramsList = params.split(",");
|
|
for (int i=0; i<paramsList.size(); i++) {
|
|
QString param = paramsList.at(i);
|
|
QStringList parList = param.split("=");
|
|
if (parList.size()>2) continue;
|
|
QString par = parList.at(0);
|
|
par = par.trimmed();
|
|
QStringList parTypeList = par.replace(QRegularExpression("[&][\\s]+"),"&").split(QRegularExpression("[\\s]+"));
|
|
if (parTypeList.size()==1) {
|
|
phpDocParams.append(parTypeList.at(0));
|
|
phpDocParamTypes.append("mixed");
|
|
} else if (parTypeList.size()==2) {
|
|
phpDocParams.append(parTypeList.at(1));
|
|
phpDocParamTypes.append(parTypeList.at(0));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData != nullptr && (blockData->state == STATE_COMMENT_ML_PHP || blockData->state == STATE_COMMENT_ML_JS || blockData->state == STATE_COMMENT_ML_CSS)) {
|
|
blockData->state = STATE_NONE;
|
|
cursor.block().setUserData(blockData);
|
|
}
|
|
cursor.beginEditBlock();
|
|
cursor.insertText("\n" + prefix + " * ");
|
|
if (isPHPDoc && mode == MODE_PHP) {
|
|
for (int i=0; i<phpDocParams.size(); i++) {
|
|
QString paramType = phpDocParamTypes.at(i);
|
|
cursor.insertText("\n" + prefix + " * @param "+paramType+" "+phpDocParams.at(i));
|
|
}
|
|
if (phpDocReturnVal.size()>0) {
|
|
cursor.insertText("\n" + prefix + " * ");
|
|
cursor.insertText("\n" + prefix + " * @return "+phpDocReturnVal);
|
|
}
|
|
}
|
|
if (isCommentOpen) cursor.insertText("\n" + prefix + " */");
|
|
cursor.endEditBlock();
|
|
if (cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor)) {
|
|
highlight->highlightChanges(cursor);
|
|
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor);
|
|
}
|
|
if (isCommentOpen) {
|
|
if (isPHPDoc) {
|
|
if (phpDocParams.size()>0) {
|
|
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor, phpDocParams.size());
|
|
}
|
|
if (phpDocReturnVal.size()>0) {
|
|
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor, 2);
|
|
}
|
|
}
|
|
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor);
|
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
setTextCursor(cursor);
|
|
} else if (isCommentLine) {
|
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
setTextCursor(cursor); // fix scroll
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
// remove indent in emply line (disabled)
|
|
if (prefix.size()>0 && blockTextTrimmed.size()==0) {
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, blockText.size());
|
|
curs.deleteChar();
|
|
}
|
|
// indent
|
|
QString indent = "";
|
|
QTextCursor cursor = textCursor();
|
|
block = cursor.block();
|
|
blockText = block.text();
|
|
total = blockText.size();
|
|
pos = cursor.positionInBlock();
|
|
QChar prevChar = '\0', nextChar = '\0', lastChar = '\0';
|
|
if (pos > 0) prevChar = blockText[pos-1];
|
|
if (pos < total) {
|
|
nextChar = blockText[pos];
|
|
if (total > pos+1) lastChar = blockText[total-1];
|
|
}
|
|
bool indentScope = false;
|
|
bool indentScopeToLast = false;
|
|
if (prevChar == "{" || prevChar == "(" || prevChar == "[") {
|
|
indent = (tabType == "spaces") ? QString(" ").repeated(tabWidth) : "\t";
|
|
if ((prevChar == "{" && (nextChar == "}" || lastChar == "}")) ||
|
|
(prevChar == "(" && (nextChar == ")" || lastChar == ")")) ||
|
|
(prevChar == "[" && (nextChar == "]" || lastChar == "]"))
|
|
) {
|
|
indentScope = true;
|
|
}
|
|
if ((prevChar == "{" && (nextChar != "}" && lastChar == "}")) ||
|
|
(prevChar == "(" && (nextChar != ")" && lastChar == ")")) ||
|
|
(prevChar == "[" && (nextChar != "]" && lastChar == "]"))
|
|
) {
|
|
indentScopeToLast = true;
|
|
}
|
|
}
|
|
// fix empty indent
|
|
if (nextChar == '\0' && prefix.size()==0 && indent.size()==0 && !isCommentClose && !isCommentLine && curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor)) {
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
block = curs.block();
|
|
blockText = block.text();
|
|
blockTextTrimmed = blockText.trimmed();
|
|
total = blockText.size();
|
|
pos = curs.positionInBlock();
|
|
while(pos < total) {
|
|
if (!curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) break;
|
|
pos = curs.positionInBlock();
|
|
QChar prevChar = blockText[pos-1];
|
|
if ((prevChar == " " || prevChar == "\t")) {
|
|
prefix += prevChar;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
curs.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor);
|
|
}
|
|
if ((prefix.size()>0 || indent.size()>0)) {
|
|
QTextCursor cursor = textCursor();
|
|
cursor.beginEditBlock();
|
|
cursor.insertText("\n"+prefix+indent);
|
|
if (indentScope) {
|
|
if (indentScopeToLast) {
|
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor);
|
|
}
|
|
cursor.insertText("\n"+prefix);
|
|
cursor.endEditBlock();
|
|
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor);
|
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
setTextCursor(cursor);
|
|
} else {
|
|
cursor.endEditBlock();
|
|
setTextCursor(cursor); // fix scroll
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
// saving mode for keyrelease event
|
|
if (code == Qt::Key_Greater && !ctrl) {
|
|
QTextCursor curs = textCursor();
|
|
QTextBlock block = curs.block();
|
|
int pos = curs.positionInBlock();
|
|
modeOnKeyPress = highlight->findModeAtCursor(&block, pos);
|
|
}
|
|
// complete popup navigation
|
|
if ((code == Qt::Key_Down || code == Qt::Key_Up) && completePopup->isVisible() && completePopup->count()>0) {
|
|
if (code == Qt::Key_Down) completePopup->selectNextItem();
|
|
if (code == Qt::Key_Up) completePopup->selectPreviousItem();
|
|
return false;
|
|
}
|
|
// hide popup
|
|
if (code == Qt::Key_Escape) {
|
|
if (completePopup->isVisible()) {
|
|
hideCompletePopup();
|
|
return false;
|
|
}
|
|
if (search->isVisible()) {
|
|
closeSearch();
|
|
return false;
|
|
}
|
|
}
|
|
// switch tooltip page
|
|
if ((code == Qt::Key_Down || code == Qt::Key_Up) && tooltipLabel.isVisible() && tooltipSavedList.size()>1 && tooltipSavedPageOffset >= 0 && tooltipSavedPageOffset < tooltipSavedList.size()) {
|
|
if (code == Qt::Key_Down) tooltipSavedPageOffset++;
|
|
if (code == Qt::Key_Up) tooltipSavedPageOffset--;
|
|
if (tooltipSavedPageOffset < 0) tooltipSavedPageOffset = tooltipSavedList.size()-1;
|
|
if (tooltipSavedPageOffset >= tooltipSavedList.size()) tooltipSavedPageOffset = 0;
|
|
tooltipSavedText = tooltipSavedList.at(tooltipSavedPageOffset);
|
|
QString fName = "", params = "";
|
|
int kSep = tooltipSavedText.indexOf("(");
|
|
if (kSep > 0) {
|
|
fName = tooltipSavedText.mid(0, kSep).trimmed();
|
|
params = tooltipSavedText.mid(kSep).trimmed();
|
|
}
|
|
QString toolTipText = tooltipSavedOrigName + " " + params;
|
|
QTextCursor cursor = textCursor();
|
|
showTooltip(& cursor, toolTipText);
|
|
followTooltip();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Editor::keyPressEvent(QKeyEvent *e)
|
|
{
|
|
if (!onKeyPress(e)) return;
|
|
QTextEdit::keyPressEvent(e);
|
|
}
|
|
|
|
bool Editor::onKeyRelease(QKeyEvent *e)
|
|
{
|
|
bool ctrl = false;
|
|
if (e->modifiers() & Qt::ControlModifier) ctrl = true;
|
|
int code = e->key();
|
|
if (code == Qt::Key_Greater && !ctrl && modeOnKeyPress == MODE_HTML) {
|
|
hideCompletePopup();
|
|
}
|
|
// fix indent on right brace insert
|
|
if (code == Qt::Key_BraceRight && !ctrl && textCursor().block().text().trimmed() == "}") {
|
|
bool foundPrefix = false;
|
|
QString prefix = "";
|
|
QChar openChar = '{';
|
|
QChar closeChar = '}';
|
|
QTextCursor cursor = textCursor();
|
|
int pos = cursor.positionInBlock()-1;
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData != nullptr && blockData->specialChars.size()>0 && blockData->specialCharsPos.size()>0 && blockData->specialChars.size()==blockData->specialCharsPos.size()) {
|
|
bool sFound = false;
|
|
int count = 0;
|
|
int positionInBlock = -1;
|
|
int iterations = 0;
|
|
do {
|
|
if (blockData->specialChars.size()>0 && blockData->specialCharsPos.size()>0) {
|
|
for (int i=blockData->specialChars.size()-1; i>=0; i--) {
|
|
iterations++;
|
|
if (iterations > SEARCH_LIMIT) break;
|
|
QChar c = blockData->specialChars.at(i);
|
|
if (!sFound && c == closeChar && blockData->specialCharsPos.at(i) == pos) {
|
|
sFound = true;
|
|
} else if (sFound && c == closeChar) {
|
|
count++;
|
|
} else if (sFound && c == openChar && count > 0) {
|
|
count--;
|
|
} else if (sFound && c == openChar && count == 0) {
|
|
positionInBlock = blockData->specialCharsPos.at(i);
|
|
int total = cursor.block().text().size();
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
do {
|
|
int pos = cursor.positionInBlock();
|
|
if (pos >= total) break;
|
|
QChar chr = cursor.block().text().at(pos);
|
|
if (chr.isSpace()) prefix += chr;
|
|
else break;
|
|
} while(cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor));
|
|
foundPrefix = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!sFound) break;
|
|
if (!cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) break;
|
|
blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData == nullptr || blockData->specialChars.size()!=blockData->specialCharsPos.size()) break;
|
|
} while(positionInBlock < 0);
|
|
}
|
|
if (foundPrefix && textCursor().block().text() != prefix + "}" && ((tabType == "spaces" && prefix.indexOf("\t") < 0) || (tabType == "tabs" && prefix.indexOf(" ") < 0))) {
|
|
QTextCursor curs = textCursor();
|
|
curs.beginEditBlock();
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
curs.deleteChar();
|
|
curs.insertText(prefix + "}");
|
|
curs.endEditBlock();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Editor::keyReleaseEvent(QKeyEvent *e)
|
|
{
|
|
if (!onKeyRelease(e)) return;
|
|
QTextEdit::keyReleaseEvent(e);
|
|
}
|
|
|
|
void Editor::inputMethodEvent(QInputMethodEvent *e)
|
|
{
|
|
QString c = e->commitString();
|
|
if (c.size() == 1) {
|
|
inputEventKey = QKeySequence::fromString(c)[0];
|
|
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
|
|
QKeyEvent * event = new QKeyEvent(QEvent::KeyPress, inputEventKey, modifiers, c);
|
|
if (!onKeyPress(event)) e->setCommitString("");
|
|
delete event;
|
|
} else {
|
|
lastKeyPressed = -1;
|
|
lastKeyPressedBlockNumber = textCursor().blockNumber();
|
|
}
|
|
QTextEdit::inputMethodEvent(e);
|
|
}
|
|
|
|
void Editor::contextMenu()
|
|
{
|
|
ignoreMouseRelease = false;
|
|
QContextMenuEvent * cEvent = new QContextMenuEvent(QContextMenuEvent::Keyboard, mapFromGlobal(QCursor::pos()));
|
|
contextMenuEvent(cEvent);
|
|
delete cEvent;
|
|
}
|
|
|
|
void Editor::insertFromMimeData(const QMimeData *source)
|
|
{
|
|
// clean, indent & insert clipboard text
|
|
if (source->hasText()) {
|
|
QTextCursor curs = textCursor();
|
|
if (curs.selectedText().size() > 0) {
|
|
int selectionStart = curs.selectionStart();
|
|
curs.setPosition(selectionStart);
|
|
}
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
int pos = curs.positionInBlock();
|
|
QString prefix = "";
|
|
while(pos < total) {
|
|
if (!curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) break;
|
|
pos = curs.positionInBlock();
|
|
QChar prevChar = blockText[pos-1];
|
|
if ((prevChar == " " || prevChar == "\t")) {
|
|
prefix += prevChar;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
QString text = source->text();
|
|
QString textF = "";
|
|
text = text.replace("\r\n","\n");
|
|
QStringList textList = text.split(QRegularExpression("[\r\n]"));
|
|
int count = 0, countBraces = 0, countParens = 0, countBrackets = 0;
|
|
if (textList.size()>0) {
|
|
QString space = (tabType == "spaces") ? QString(" ").repeated(tabWidth) : "\t";
|
|
if (textList.size()>1) {
|
|
QString line = textList.at(0);
|
|
textF += line.trimmed() + "\n";
|
|
for (int i=0; i<textList.size(); i++) {
|
|
line = textList.at(i);
|
|
line = line.trimmed();
|
|
if (line.size()==0) {
|
|
if (i>0) textF += prefix + space.repeated(count) + "\n";
|
|
continue;
|
|
}
|
|
QChar first = line[0];
|
|
QChar last = line[line.size()-1];
|
|
if ((first == "}" && countBraces > 0) ||
|
|
(first == ")" && countParens > 0) ||
|
|
(first == "]" && countBrackets > 0)
|
|
) {
|
|
count--;
|
|
if (count < 0) count = 0;
|
|
if (first == "}") countBraces--;
|
|
if (first == ")") countParens--;
|
|
if (first == "]") countBrackets--;
|
|
if (countBraces < 0) countBraces = 0;
|
|
if (countParens < 0) countParens = 0;
|
|
if (countBrackets < 0) countBrackets = 0;
|
|
}
|
|
if (i>0) {
|
|
textF += prefix + space.repeated(count) + line + "\n";
|
|
}
|
|
if ((last == "{" || last == "(" || last == "[") && first != "/") {
|
|
count++;
|
|
if (last == "{") countBraces++;
|
|
else if (last == "(") countParens++;
|
|
else if (last == "[") countBrackets++;
|
|
}
|
|
}
|
|
textF = textF.trimmed();
|
|
} else {
|
|
textF = text;
|
|
}
|
|
QTextCursor cursor = textCursor();
|
|
int startBlockNumber = cursor.block().blockNumber();
|
|
if (cursor.selectedText().size() > 0) {
|
|
int selectionStart = cursor.selectionStart();
|
|
QTextCursor curs = textCursor();
|
|
curs.setPosition(selectionStart);
|
|
startBlockNumber = curs.block().blockNumber();
|
|
}
|
|
if (tabType == "spaces") textF = textF.replace("\t", space);
|
|
cursor.insertText(textF);
|
|
int endBlockNumber = cursor.block().blockNumber();
|
|
if (endBlockNumber > startBlockNumber) {
|
|
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor, endBlockNumber - startBlockNumber);
|
|
}
|
|
QTextBlock block = cursor.block();
|
|
highlight->resetHighlightBlock(block);
|
|
highlight->highlightChanges(cursor);
|
|
do {
|
|
block = cursor.block();
|
|
if (!block.isValid()) break;
|
|
// modified state (no need anymore)
|
|
/*
|
|
modifiedLinesIterator = modifiedLines.find(cursor.block().blockNumber() + 1);
|
|
if (modifiedLinesIterator == modifiedLines.end()) {
|
|
modifiedLines[cursor.block().blockNumber() + 1] = cursor.block().blockNumber() + 1;
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData != nullptr) {
|
|
blockData->isModified = true;
|
|
cursor.block().setUserData(blockData);
|
|
}
|
|
}
|
|
*/
|
|
// spell check
|
|
if (spellCheckerEnabled && spellChecker != nullptr && !isBigFile) {
|
|
spellPastedBlocksQueue.append(cursor.block().blockNumber());
|
|
}
|
|
if (cursor.block().blockNumber() == endBlockNumber) break;
|
|
} while(cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor));
|
|
lineNumber->update();
|
|
if (spellCheckerEnabled && spellChecker != nullptr && !isBigFile) {
|
|
/*
|
|
if (spellPastedBlocksQueue.size() > 1) {
|
|
std::reverse(spellPastedBlocksQueue.begin(), spellPastedBlocksQueue.end());
|
|
}
|
|
*/
|
|
QTimer::singleShot(INTERVAL_SPELL_CHECK_MILLISECONDS, this, SLOT(spellCheckPasted()));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
QTextEdit::insertFromMimeData(source);
|
|
}
|
|
|
|
void Editor::blockCountChanged(int /*blockCount*/)
|
|
{
|
|
updateViewportMargins();
|
|
updateLineWidgetsArea();
|
|
lineAnnotation->hide();
|
|
// updating mark points, modified lines
|
|
markPoints.clear();
|
|
modifiedLines.clear();
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
do {
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(curs.block().userData());
|
|
if (blockData != nullptr && blockData->hasMarkPoint) {
|
|
markPoints[curs.block().blockNumber()+1] = curs.block().text().toStdString();
|
|
}
|
|
if (blockData != nullptr && blockData->isModified) {
|
|
modifiedLines[curs.block().blockNumber()+1] = curs.block().blockNumber()+1;
|
|
}
|
|
} while(curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor));
|
|
|
|
emit statusBarText(tabIndex, ""); // update status bar
|
|
}
|
|
|
|
void Editor::horizontalScrollbarValueChanged(int /* sliderPos */)
|
|
{
|
|
updateLineAnnotationView();
|
|
if (!searchDisplayOnTop && static_cast<Search *>(search)->isVisible()) {
|
|
static_cast<Search *>(search)->updateScrollBar();
|
|
}
|
|
}
|
|
|
|
void Editor::verticalScrollbarValueChanged(int /* sliderPos */)
|
|
{
|
|
firstVisibleBlockIndex = -1;
|
|
lastVisibleBlockIndex = -1;
|
|
|
|
hidePopups();
|
|
updateLineWidgetsArea();
|
|
updateLineAnnotationView();
|
|
lineMap->update();
|
|
|
|
if (is_ready && !scrollBarValueChangeLocked) {
|
|
scrollBarValueChangeLocked = true;
|
|
QTimer::singleShot(INTERVAL_SCROLL_HIGHLIGHT_MILLISECONDS, this, SLOT(verticalScrollbarValueChangedDelayed()));
|
|
}
|
|
}
|
|
|
|
void Editor::verticalScrollbarValueChangedDelayed()
|
|
{
|
|
highlight->updateBlocks(getLastVisibleBlockIndex());
|
|
scrollBarValueChangeLocked = false;
|
|
}
|
|
|
|
void Editor::mousePressEvent(QMouseEvent *e)
|
|
{
|
|
if (isMultiSelectMode) multiSelectToggle();
|
|
hideCompletePopup();
|
|
#if defined(Q_OS_ANDROID)
|
|
mousePressTimer.start();
|
|
// hide virtual keyboard if visible
|
|
if (QApplication::inputMethod()->isVisible()) {
|
|
QApplication::inputMethod()->hide();
|
|
QApplication::inputMethod()->reset();
|
|
ignoreMouseRelease = true;
|
|
return;
|
|
}
|
|
#endif
|
|
QTextEdit::mousePressEvent(e);
|
|
}
|
|
|
|
void Editor::mouseMoveEvent(QMouseEvent *e)
|
|
{
|
|
/*
|
|
#if defined(Q_OS_ANDROID)
|
|
if (mousePressTimer.isActive()) mousePressTimer.stop();
|
|
#endif
|
|
*/
|
|
QTextEdit::mouseMoveEvent(e);
|
|
}
|
|
|
|
void Editor::mouseReleaseEvent(QMouseEvent *e)
|
|
{
|
|
if (e->modifiers() & Qt::ControlModifier) {
|
|
showDeclarationRequested();
|
|
} else {
|
|
hideTooltip();
|
|
}
|
|
#if defined(Q_OS_ANDROID)
|
|
if (mousePressTimer.isActive()) mousePressTimer.stop();
|
|
if (ignoreMouseRelease) {
|
|
ignoreMouseRelease = false;
|
|
return;
|
|
}
|
|
#endif
|
|
QTextEdit::mouseReleaseEvent(e);
|
|
}
|
|
|
|
void Editor::wheelEvent(QWheelEvent *e)
|
|
{
|
|
#if defined(Q_OS_ANDROID)
|
|
if (isGesturesEnabled) {
|
|
disableGestures();
|
|
}
|
|
#endif
|
|
QTextEdit::wheelEvent(e);
|
|
}
|
|
|
|
void Editor::contentsChange(int position, int charsRemoved, int charsAdded)
|
|
{
|
|
if (!is_ready || isReadOnly()) return;
|
|
QTextBlock block = document()->findBlock(position);
|
|
if (!block.isValid()) return;
|
|
|
|
highlight->resetHighlightBlock(block);
|
|
|
|
QTextBlock lastBlock = document()->findBlock(position + charsAdded + (charsRemoved > 0 ? 1 : 0));
|
|
if (!lastBlock.isValid()) {
|
|
QTextCursor curs = QTextCursor(block);
|
|
highlight->highlightChanges(curs);
|
|
return;
|
|
}
|
|
|
|
int endPosition = lastBlock.position() + lastBlock.length();
|
|
bool forceHighlightOfNextBlock = false;
|
|
while (block.isValid() && (block.position() < endPosition)) {
|
|
const int stateBeforeHighlight = block.userState();
|
|
highlight->rehighlightBlock(block);
|
|
forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight);
|
|
block = block.next();
|
|
}
|
|
if (forceHighlightOfNextBlock && block.isValid()) {
|
|
QTextCursor curs = QTextCursor(block);
|
|
highlight->highlightChanges(curs);
|
|
}
|
|
}
|
|
|
|
void Editor::textChanged()
|
|
{
|
|
if (!is_ready || isReadOnly()) return;
|
|
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
|
|
bool ctrl = modifiers & Qt::ControlModifier;
|
|
if (!textChangeLocked && lastKeyPressed != Qt::Key_Backspace && lastKeyPressed != Qt::Key_Delete && lastKeyPressed != Qt::Key_Return && lastKeyPressed != Qt::Key_Tab && lastKeyPressed != Qt::Key_Space && !ctrl) {
|
|
textChangeLocked = true;
|
|
QTimer::singleShot(INTERVAL_TEXT_CHANGED_MILLISECONDS, this, SLOT(textChangedDelayed()));
|
|
} else if (!textChangeLocked) {
|
|
hideCompletePopup();
|
|
}
|
|
bool _modified = modified;
|
|
if (document()->isModified()) {
|
|
modified = true;
|
|
} else {
|
|
modified = false;
|
|
}
|
|
if (_modified != modified) {
|
|
emit modifiedStateChanged(tabIndex, modified);
|
|
}
|
|
|
|
// parse
|
|
if (!parseLocked && isReady()) {
|
|
parseLocked = true;
|
|
QTimer::singleShot(parseResultChangedDelay, this, SLOT(parseResultChanged()));
|
|
}
|
|
|
|
// spell check
|
|
if (spellCheckerEnabled && spellChecker != nullptr) {
|
|
if (spellBlocksQueue.size() == 0 || (spellBlocksQueue.last() != textCursor().block().blockNumber())) {
|
|
spellBlocksQueue.append(textCursor().block().blockNumber());
|
|
}
|
|
if (!spellLocked) {
|
|
spellLocked = true;
|
|
QTimer::singleShot(INTERVAL_SPELL_CHECK_MILLISECONDS, this, SLOT(spellCheck()));
|
|
}
|
|
}
|
|
|
|
// set line modified status
|
|
QTextCursor curs = textCursor();
|
|
if (lastKeyPressedBlockNumber >= curs.block().blockNumber() && !ctrl) {
|
|
modifiedLinesIterator = modifiedLines.find(curs.block().blockNumber() + 1);
|
|
if (modifiedLinesIterator == modifiedLines.end()) {
|
|
modifiedLines[curs.block().blockNumber() + 1] = curs.block().blockNumber() + 1;
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(curs.block().userData());
|
|
if (blockData != nullptr) {
|
|
blockData->isModified = true;
|
|
curs.block().setUserData(blockData);
|
|
}
|
|
lineNumber->update();
|
|
}
|
|
} else if (lastKeyPressedBlockNumber < curs.block().blockNumber()) {
|
|
modifiedLinesIterator = modifiedLines.find(lastKeyPressedBlockNumber + 1);
|
|
if (modifiedLinesIterator == modifiedLines.end()) {
|
|
modifiedLines[lastKeyPressedBlockNumber + 1] = lastKeyPressedBlockNumber + 1;
|
|
if (lastKeyPressedBlockNumber == curs.block().blockNumber()-1) {
|
|
curs.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor);
|
|
} else {
|
|
curs.movePosition(QTextCursor::Start);
|
|
if (lastKeyPressedBlockNumber > 0) curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, lastKeyPressedBlockNumber);
|
|
}
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(curs.block().userData());
|
|
if (blockData != nullptr) {
|
|
blockData->isModified = true;
|
|
curs.block().setUserData(blockData);
|
|
}
|
|
lineNumber->update();
|
|
}
|
|
}
|
|
// workaround for Android
|
|
if (inputEventKey >= 0) {
|
|
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
|
|
QKeyEvent * event = new QKeyEvent(QEvent::KeyRelease, inputEventKey, modifiers);
|
|
onKeyRelease(event);
|
|
delete event;
|
|
inputEventKey = -1;
|
|
}
|
|
}
|
|
|
|
void Editor::textChangedDelayed()
|
|
{
|
|
textChangeLocked = false;
|
|
// complete popup
|
|
QTextCursor curs = textCursor();
|
|
if (curs.selectedText().size()!=0) return;
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
int pos = curs.positionInBlock();
|
|
// if (highlight->isStateOpen(&block, pos)) {
|
|
// hideCompletePopup();
|
|
// return;
|
|
// }
|
|
std::string mode = highlight->findModeAtCursor(&block, pos);
|
|
int state = highlight->findStateAtCursor(&block, pos);
|
|
if (mode != MODE_HTML && state != STATE_NONE) return;
|
|
//if (mode == MODE_HTML && state != STATE_TAG) return; // snippets won't work
|
|
if (mode == MODE_UNKNOWN) return;
|
|
QChar prevChar = '\0', nextChar = '\0';
|
|
if (pos > 0 && curs.selectedText().size()==0) prevChar = blockText[pos - 1];
|
|
if (pos < total && curs.selectedText().size()==0) nextChar = blockText[pos];
|
|
// text till cursor
|
|
QString cursorText = "", prevText = "";
|
|
QChar cursorTextPrevChar = '\0';
|
|
int cursorTextPos = pos;
|
|
for (int i=pos; i>0; i--) {
|
|
QString c = blockText.mid(i-1, 1);
|
|
cursorTextPrevChar = c[0];
|
|
if (mode == MODE_PHP || mode == MODE_JS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else {
|
|
if (isalnum(c[0].toLatin1()) || c=="_") prevText = c + prevText;
|
|
else break;
|
|
}
|
|
cursorTextPos = i-1;
|
|
}
|
|
if (prevText.size()>0) {
|
|
QChar c = prevText[0];
|
|
if (isalpha(c.toLatin1())) cursorText = prevText;
|
|
else if ((mode == MODE_PHP || mode == MODE_JS) && (c=="$" || c=="_")) cursorText = prevText;
|
|
else if (mode == MODE_CSS && (c=="#" || c=="." || c=="-" || c=="_")) cursorText = prevText;
|
|
else if (c=="_") cursorText = prevText;
|
|
}
|
|
hideCompletePopup();
|
|
if (cursorText.size() > 0 && (nextChar == '\0' || !isalnum(nextChar.toLatin1()))) {
|
|
detectCompleteText(cursorText, cursorTextPrevChar, cursorTextPos, mode, state);
|
|
}
|
|
}
|
|
|
|
bool Editor::isKnownWord(QString word)
|
|
{
|
|
bool known = false;
|
|
SW->wordsIterator = SW->words.find(word.toLower().toStdString());
|
|
if (SW->wordsIterator != SW->words.end()) {
|
|
known = true;
|
|
}
|
|
if (!known) {
|
|
SW->wordsCSIterator = SW->wordsCS.find(word.toStdString());
|
|
if (SW->wordsCSIterator != SW->wordsCS.end()) {
|
|
known = true;
|
|
}
|
|
}
|
|
if (!known) {
|
|
HW->phpwordsIterator = HW->phpwords.find(word.toLower().toStdString());
|
|
if (HW->phpwordsIterator != HW->phpwords.end()) {
|
|
known = true;
|
|
}
|
|
}
|
|
if (!known) {
|
|
HW->phpwordsCSIterator = HW->phpwordsCS.find(word.toStdString());
|
|
if (HW->phpwordsCSIterator != HW->phpwordsCS.end()) {
|
|
known = true;
|
|
}
|
|
}
|
|
if (!known) {
|
|
HW->jswordsCSIterator = HW->jswordsCS.find(word.toStdString());
|
|
if (HW->jswordsCSIterator != HW->jswordsCS.end()) {
|
|
known = true;
|
|
}
|
|
}
|
|
if (!known) {
|
|
HW->csswordsIterator = HW->csswords.find(word.toLower().toStdString());
|
|
if (HW->csswordsIterator != HW->csswords.end()) {
|
|
known = true;
|
|
}
|
|
}
|
|
if (!known) {
|
|
HW->htmlwordsIterator = HW->htmlwords.find(word.toLower().toStdString());
|
|
if (HW->htmlwordsIterator != HW->htmlwords.end()) {
|
|
known = true;
|
|
}
|
|
}
|
|
return known;
|
|
}
|
|
|
|
void Editor::spellCheck(bool suggest, bool forceRehighlight)
|
|
{
|
|
spellLocked = false;
|
|
if (!spellCheckerEnabled || spellChecker == nullptr) return;
|
|
QTextCursor cursor = textCursor();
|
|
int pos = cursor.positionInBlock();
|
|
QString blockText = cursor.block().text();
|
|
// text till cursor
|
|
QString cursorText = "";
|
|
int cursorTextPos = pos;
|
|
if (suggest) {
|
|
for (int i=pos; i>0; i--) {
|
|
QChar c = blockText[i-1];
|
|
if (c.isLetter() || c=="_") cursorText = c + cursorText;
|
|
else break;
|
|
cursorTextPos = i-1;
|
|
}
|
|
}
|
|
QRegularExpressionMatch m;
|
|
for (int i=0; i<spellBlocksQueue.size(); i++) {
|
|
bool misspelled = false;
|
|
int blockNumber = spellBlocksQueue.at(i);
|
|
if (blockNumber == cursor.block().blockNumber() + 1) {
|
|
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
|
|
} else {
|
|
cursor.movePosition(QTextCursor::Start);
|
|
if (blockNumber > 0) cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, blockNumber);
|
|
}
|
|
QTextBlock block = cursor.block();
|
|
if (block.blockNumber() != spellBlocksQueue.at(i)) continue;
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(block.userData());
|
|
if (blockData == nullptr) continue;
|
|
blockData->spellStarts.clear();
|
|
blockData->spellLengths.clear();
|
|
QString blockText = cursor.block().text();
|
|
if (blockText.trimmed().size() == 0) continue;
|
|
int offset = 0;
|
|
do {
|
|
m = spellWordExpr.match(blockText, offset);
|
|
if (m.capturedStart() >= 0) {
|
|
offset = m.capturedStart() + m.capturedLength();
|
|
int start = m.capturedStart(1);
|
|
int length = m.capturedLength(1);
|
|
QString word = blockText.mid(start, length);
|
|
bool hasLetter = false;
|
|
for (int i=0; i<word.size(); i++) {
|
|
QChar c = word[i];
|
|
if (c.isLetter()) hasLetter = true;
|
|
}
|
|
if (!hasLetter) continue;
|
|
if (word.size() > 0 && word[0] == "$") continue;
|
|
if (word.size()>1 && word[0]=='\'' && word[word.size()-1]=='\'') {
|
|
word = word.mid(1,word.size()-2);
|
|
start += 1;
|
|
length -= 2;
|
|
}
|
|
if (word.size() > 1 && word[word.size()-1] == "$") {
|
|
word = word.mid(0, word.size()-1);
|
|
length -= 1;
|
|
}
|
|
if (word.size() < 2) continue;
|
|
std::string mode = highlight->findModeAtCursor(&block, start);
|
|
int state = highlight->findStateAtCursor(&block, start);
|
|
//if ((mode == MODE_PHP || mode == MODE_JS || mode == MODE_CSS) && state == STATE_NONE) continue;
|
|
if (mode == MODE_PHP && state != STATE_COMMENT_ML_PHP) continue;
|
|
if (mode == MODE_JS && state != STATE_COMMENT_ML_JS) continue;
|
|
if (mode == MODE_CSS && state != STATE_COMMENT_ML_CSS) continue;
|
|
if (mode == MODE_HTML && state != STATE_NONE) continue;
|
|
if (mode == MODE_UNKNOWN && !highlight->isTextMode() && state != STATE_COMMENT_ML_UNKNOWN) continue;
|
|
bool doSuggest = suggest;
|
|
if (word.size() < 4) doSuggest = false;
|
|
if (mode != MODE_HTML && mode != MODE_UNKNOWN) doSuggest = false;
|
|
if (lastKeyPressed == Qt::Key_Backspace || lastKeyPressed == Qt::Key_Delete) doSuggest = false;
|
|
if (!isKnownWord(word) && !spellChecker->check(word)) {
|
|
misspelled = true;
|
|
blockData->spellStarts.append(start);
|
|
blockData->spellLengths.append(length);
|
|
if (doSuggest && word == cursorText && start == cursorTextPos) {
|
|
hideCompletePopup();
|
|
QStringList suggestions = spellChecker->suggest(word);
|
|
suggestWords(suggestions, cursorTextPos);
|
|
}
|
|
}
|
|
}
|
|
} while(m.capturedStart() >= 0);
|
|
block.setUserData(blockData);
|
|
if (forceRehighlight || misspelled) {
|
|
blockSignals(true);
|
|
highlight->rehighlightBlock(block);
|
|
blockSignals(false);
|
|
}
|
|
}
|
|
spellBlocksQueue.clear();
|
|
}
|
|
|
|
QChar Editor::findPrevCharNonSpaceAtCursos(QTextCursor & curs)
|
|
{
|
|
QChar prevChar = '\0';
|
|
int cursPos = curs.position();
|
|
while(curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor)) {
|
|
QString blockText = curs.block().text();
|
|
int pos = curs.positionInBlock();
|
|
if (pos >= blockText.size()) continue;
|
|
prevChar = blockText[pos];
|
|
if (!prevChar.isSpace()) {
|
|
break;
|
|
}
|
|
}
|
|
if (prevChar!="." && prevChar!="-" && prevChar!=">" && prevChar!=":" && prevChar!="\\") {
|
|
curs.setPosition(cursPos);
|
|
}
|
|
return prevChar;
|
|
}
|
|
|
|
QChar Editor::findNextCharNonSpaceAtCursos(QTextCursor & curs)
|
|
{
|
|
QChar nextChar = '\0';
|
|
int cursPos = curs.position();
|
|
while(curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) {
|
|
QString blockText = curs.block().text();
|
|
int pos = curs.positionInBlock();
|
|
if (pos >= blockText.size()) continue;
|
|
nextChar = blockText[pos];
|
|
if (!nextChar.isSpace()) {
|
|
break;
|
|
}
|
|
}
|
|
if (nextChar!="." && nextChar!="-" && nextChar!=">" && nextChar!=":" && nextChar!="\\") {
|
|
curs.setPosition(cursPos);
|
|
}
|
|
return nextChar;
|
|
}
|
|
|
|
QString Editor::findPrevWordNonSpaceAtCursor(QTextCursor & curs, std::string mode)
|
|
{
|
|
QString prevText = "";
|
|
while(curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor)) {
|
|
QString blockText = curs.block().text();
|
|
int pos = curs.positionInBlock();
|
|
if (pos >= blockText.size()) continue;
|
|
QChar c = blockText[pos];
|
|
if (prevText.size() == 0 && c.isSpace()) continue;
|
|
if (mode == MODE_PHP || mode == MODE_JS) {
|
|
if (isalnum(c.toLatin1()) || c=="$" || c=="_") {
|
|
prevText = c + prevText;
|
|
} else {
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
|
|
break;
|
|
}
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c.toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") {
|
|
prevText = c + prevText;
|
|
} else {
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
|
|
break;
|
|
}
|
|
} else {
|
|
if (isalnum(c.toLatin1()) || c=="_") {
|
|
prevText = c + prevText;
|
|
} else {
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
|
|
break;
|
|
}
|
|
}
|
|
if (prevText.size() > 0 && pos == 0) break;
|
|
}
|
|
return prevText;
|
|
}
|
|
|
|
QString Editor::findNextWordNonSpaceAtCursor(QTextCursor & curs, std::string mode)
|
|
{
|
|
QString nextText = "";
|
|
while(curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) {
|
|
QString blockText = curs.block().text();
|
|
int pos = curs.positionInBlock();
|
|
if (pos >= blockText.size()) continue;
|
|
QChar c = blockText[pos];
|
|
if (nextText.size() == 0 && c.isSpace()) continue;
|
|
if (mode == MODE_PHP || mode == MODE_JS) {
|
|
if (isalnum(c.toLatin1()) || c=="$" || c=="_") {
|
|
nextText += c;
|
|
} else {
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor);
|
|
break;
|
|
}
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c.toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") {
|
|
nextText += c;
|
|
} else {
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor);
|
|
break;
|
|
}
|
|
} else {
|
|
if (isalnum(c.toLatin1()) || c=="_") {
|
|
nextText += c;
|
|
} else {
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor);
|
|
break;
|
|
}
|
|
}
|
|
if (nextText.size() > 0 && pos == 0) break;
|
|
}
|
|
return nextText;
|
|
}
|
|
|
|
QString Editor::completeClassNamePHPAtCursor(QTextCursor & curs, QString prevWord, QString nsName)
|
|
{
|
|
if (!parsePHPEnabled || prevWord.size() == 0) return prevWord;
|
|
QString _clsName = prevWord;
|
|
QChar _prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
if (_prevChar == "\\") {
|
|
do {
|
|
QString _prevWord = findPrevWordNonSpaceAtCursor(curs, MODE_PHP);
|
|
if (_prevWord.size() == 0 || _prevWord[0] == "$" || _prevWord == "new") {
|
|
_clsName = "\\" + _clsName;
|
|
break;
|
|
}
|
|
_clsName = _prevWord + "\\" + _clsName;
|
|
_prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
} while(_prevChar == "\\");
|
|
}
|
|
QString clsAlias = "";
|
|
if (_clsName.indexOf("\\") < 0) clsAlias = _clsName;
|
|
if (_clsName[0] != "\\") clsAlias = _clsName.mid(0, _clsName.indexOf("\\"));
|
|
if (clsAlias == "namespace") {
|
|
QString ns = "";
|
|
if (nsName.size() > 0) ns = "\\" + nsName;
|
|
_clsName = ns + _clsName.mid(clsAlias.size());
|
|
clsAlias = "";
|
|
}
|
|
if (clsAlias.size() > 0) {
|
|
// search imports
|
|
ParsePHP::ParseResultNamespace ns;
|
|
ParsePHP::ParseResultImport imp;
|
|
for (int i=0; i<parseResultPHP.namespaces.size(); i++) {
|
|
ParsePHP::ParseResultNamespace _ns = parseResultPHP.namespaces.at(i);
|
|
if (_ns.name == nsName) {
|
|
ns = _ns;
|
|
break;
|
|
}
|
|
}
|
|
for (int c=0; c<ns.importsIndexes.size(); c++) {
|
|
if (parseResultPHP.imports.size() <= ns.importsIndexes.at(c)) break;
|
|
ParsePHP::ParseResultImport _imp = parseResultPHP.imports.at(ns.importsIndexes.at(c));
|
|
if (_imp.type == IMPORT_TYPE_CLASS && _imp.name == clsAlias) {
|
|
imp = _imp;
|
|
break;
|
|
}
|
|
}
|
|
if (ns.name==nsName && imp.name==clsAlias) {
|
|
if (_clsName.indexOf("\\") < 0) _clsName = imp.path;
|
|
if (_clsName[0] != "\\") _clsName = imp.path+_clsName.mid(_clsName.indexOf("\\"));
|
|
}
|
|
}
|
|
if (_clsName[0] != "\\") {
|
|
QString ns = "\\";
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
_clsName = ns + _clsName;
|
|
}
|
|
if (_clsName[0] == "\\") _clsName = _clsName.mid(1);
|
|
return _clsName;
|
|
}
|
|
|
|
void Editor::showCompletePopup()
|
|
{
|
|
int blockHeight = static_cast<int>(document()->documentLayout()->blockBoundingRect(textCursor().block()).height());
|
|
int viewLeft = viewport()->geometry().left();
|
|
int viewTop = viewport()->geometry().top();
|
|
int viewWidth = viewport()->geometry().width();
|
|
int viewHeight = viewport()->geometry().height();
|
|
int cursLeft = cursorRect().left();
|
|
int cursTop = cursorRect().top();
|
|
|
|
QTextCursor curs = textCursor();
|
|
int cursorTextPos = completePopup->getTextStartPos();
|
|
if (cursorTextPos >= 0 && cursorTextPos < curs.positionInBlock()) {
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, curs.positionInBlock() - cursorTextPos);
|
|
cursLeft = cursorRect(curs).left();
|
|
}
|
|
if (curs.block().layout() != nullptr && curs.block().layout()->lineCount() > 1) {
|
|
QTextLine line = curs.block().layout()->lineForTextPosition(curs.positionInBlock());
|
|
if (line.isValid()) {
|
|
blockHeight = static_cast<int>(line.height());
|
|
}
|
|
}
|
|
|
|
completePopup->showPopup(cursLeft, cursTop, viewLeft, viewTop, viewWidth, viewHeight, blockHeight);
|
|
}
|
|
|
|
void Editor::hideCompletePopup()
|
|
{
|
|
completePopup->hidePopup();
|
|
completePopup->clearItems();
|
|
}
|
|
|
|
void Editor::detectCompleteTextHTML(QString text, QChar cursorTextPrevChar, int state)
|
|
{
|
|
// snippets
|
|
if (cursorTextPrevChar == "@" && SNP->htmlSnippets.contains(text)) {
|
|
completePopup->addItem(SNIPPET_PREFIX+text, SNP->htmlSnippets[text]);
|
|
}
|
|
if (state != STATE_TAG) return;
|
|
if ((cursorTextPrevChar == "<" || cursorTextPrevChar == "/") && completePopup->count() < completePopup->limit()) {
|
|
// html tags
|
|
for (auto & it : CW->htmlAllTagsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// events
|
|
if (state == STATE_TAG && completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->jsEventsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::detectCompleteTextCSS(QString text, QChar cursorTextPrevChar)
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
int pos = curs.positionInBlock();
|
|
QString blockText = curs.block().text();
|
|
QString blockTextTillCursos = blockText.mid(0, pos);
|
|
int braceOpens = blockTextTillCursos.count("{");
|
|
int braceCloses = blockTextTillCursos.count("}");
|
|
int braces = braceOpens - braceCloses;
|
|
bool cssMediaScope = false;
|
|
if (curs.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) {
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(curs.block().userData());
|
|
if (blockData != nullptr) {
|
|
braces += blockData->bracesCSS;
|
|
cssMediaScope = blockData->cssMediaScope;
|
|
}
|
|
}
|
|
int propOffset = blockTextTillCursos.lastIndexOf(";");
|
|
if (propOffset < 0) propOffset = 0;
|
|
int colIndex = blockTextTillCursos.indexOf(":", propOffset);
|
|
// snippets
|
|
if (cursorTextPrevChar == "@" && SNP->cssSnippets.contains(text)) {
|
|
completePopup->addItem(SNIPPET_PREFIX+text, SNP->cssSnippets[text]);
|
|
}
|
|
if (((braces > 0 && !cssMediaScope) || (braces > 1 && cssMediaScope)) && colIndex < 0 && completePopup->count() < completePopup->limit()) {
|
|
// css props
|
|
for (auto & it : CW->cssPropertiesComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
if (colIndex >= 0 && completePopup->count() < completePopup->limit()) {
|
|
// css vals
|
|
for (auto & it : CW->cssValuesComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// css id & class selectors
|
|
for (int i=parseResultCSS.names.size()-1; i>=0; i--){
|
|
ParseCSS::ParseResultName _name = parseResultCSS.names.at(i);
|
|
QString k = _name.name;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(k, k);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// html tags
|
|
for (auto & it : CW->htmlAllTagsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
if (cursorTextPrevChar == ":" && completePopup->count() < completePopup->limit()) {
|
|
// css pseudo
|
|
for (auto & it : CW->cssPseudoComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::detectCompleteTextJS(QString text, int cursorTextPos, QChar cursorTextPrevChar, QString jsExtMode)
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
if (cursorTextPos > 0) curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QChar prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QString prevWord = findPrevWordNonSpaceAtCursor(curs, MODE_JS);
|
|
// global context
|
|
if (prevChar != "." && prevWord != "function") {
|
|
// snippets
|
|
if (cursorTextPrevChar == "@" && SNP->jsSnippets.contains(text)) {
|
|
completePopup->addItem(SNIPPET_PREFIX+text, SNP->jsSnippets[text]);
|
|
}
|
|
// js specials
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->jsSpecialsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
if (jsExtMode.isEmpty()) {
|
|
// js objects
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->jsObjectsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// js functions
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->jsFunctionsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// js interfaces
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->jsInterfacesComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
detectCompleteTextJSExt(text, jsExtMode);
|
|
}
|
|
// parsed classes
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (int i=0; i<parseResultJS.classes.size(); i++){
|
|
ParseJS::ParseResultClass cls = parseResultJS.classes.at(i);
|
|
QString k = cls.name;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(k, k);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// parsed functions
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (int i=0; i<parseResultJS.functions.size(); i++){
|
|
ParseJS::ParseResultFunction func = parseResultJS.functions.at(i);
|
|
if (func.clsName.size() > 0) continue;
|
|
QString k = func.name;
|
|
QString p = "( " + func.args + " )";
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(k, p);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// parsed vars
|
|
std::unordered_map<std::string, std::string> vars;
|
|
std::unordered_map<std::string, std::string>::iterator varsIterator;
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (int i=parseResultJS.variables.size()-1; i>=0; i--){
|
|
ParseJS::ParseResultVariable _variable = parseResultJS.variables.at(i);
|
|
//if (_variable.clsName.size() > 0) continue;
|
|
QString k = _variable.name;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
varsIterator = vars.find(k.toStdString());
|
|
if (varsIterator == vars.end()) {
|
|
vars[k.toStdString()] = k.toStdString();
|
|
completePopup->addItem(k, k);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// highlighted vars
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(curs.block().userData());
|
|
if (blockData != nullptr && blockData->varsChainJS.size()>0 && (blockData->varsChainJS.indexOf(text, 0, Qt::CaseInsensitive)==0 || blockData->varsChainJS.indexOf(","+text, 0, Qt::CaseInsensitive)>0)) {
|
|
QStringList varsList = blockData->varsChainJS.split(",");
|
|
for (QString k : varsList) {
|
|
if (k == text) continue; // need this
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
varsIterator = vars.find(k.toStdString());
|
|
if (varsIterator == vars.end()) {
|
|
vars[k.toStdString()] = k.toStdString();
|
|
completePopup->addItem(k, k);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (prevChar == ".") {
|
|
// object context
|
|
QString k = "prototype";
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(k, k);
|
|
}
|
|
// methods
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->jsMethodsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// events
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->jsEventsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// parsed functions
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (int i=0; i<parseResultJS.functions.size(); i++){
|
|
ParseJS::ParseResultFunction func = parseResultJS.functions.at(i);
|
|
if (func.clsName.size() == 0) continue;
|
|
QString k = func.name;
|
|
QString p = "( " + func.args + " )";
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(k, p);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// parsed props
|
|
std::unordered_map<std::string, std::string> vars;
|
|
std::unordered_map<std::string, std::string>::iterator varsIterator;
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (int i=0; i<parseResultJS.variables.size(); i++){
|
|
ParseJS::ParseResultVariable _variable = parseResultJS.variables.at(i);
|
|
if (_variable.clsName.size() == 0) continue;
|
|
QString k = _variable.name;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
varsIterator = vars.find(k.toStdString());
|
|
if (varsIterator == vars.end()) {
|
|
vars[k.toStdString()] = k.toStdString();
|
|
completePopup->addItem(k, k);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::detectCompleteTextJSExt(QString text, QString jsExtMode)
|
|
{
|
|
if (jsExtMode == EXTENSION_DART) {
|
|
// flutter classes
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->flutterObjectsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// dart classes
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->dartObjectsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// flutter functions
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->flutterFunctionsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// dart functions
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->dartFunctionsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::detectCompleteTextPHPGlobalContext(QString text, int cursorTextPos, QChar prevChar, QChar prevPrevChar, QString prevWord, QTextCursor curs, QChar cursorTextPrevChar)
|
|
{
|
|
bool completeDetectedPHP = false;
|
|
QTextCursor cursor = textCursor();
|
|
QTextBlock block = cursor.block();
|
|
QString nsName = highlight->findNsPHPAtCursor(& block, cursorTextPos);
|
|
QString clsName = highlight->findClsPHPAtCursor(& block, cursorTextPos);
|
|
QString funcName = highlight->findFuncPHPAtCursor(& block, cursorTextPos);
|
|
if (text[0] != "$" && prevWord != "function" && prevWord != "class" && prevWord != "interface" && prevWord != "trait" && prevWord != "namespace" && prevWord != "use") {
|
|
if (prevWord != "new" && (prevChar != "?" || text != "php") && (prevChar != ":" || prevPrevChar != ":")) {
|
|
// snippets
|
|
if (cursorTextPrevChar == "@" && SNP->phpSnippets.contains(text)) {
|
|
completePopup->addItem(SNIPPET_PREFIX+text, SNP->phpSnippets[text]);
|
|
}
|
|
// php specials
|
|
if (prevChar != "\\" && completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->phpSpecialsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// php functions
|
|
if (prevChar != "\\" && completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->phpFunctionsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second), TOOLTIP_DELIMITER);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// php consts
|
|
if (prevChar != "\\" && completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->phpConstsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
// detect class name
|
|
QString _clsName = "";
|
|
if (prevChar == "\\") {
|
|
_clsName = completeClassNamePHPAtCursor(curs, prevWord, nsName);
|
|
}
|
|
// php classes (without params)
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->phpClassesComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
QString _text = nsName.size() > 0 ? nsName + "\\" + text : text;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
QString name = QString::fromStdString(it.first);
|
|
if (_clsName.size() > 0 && name.indexOf(_clsName+"\\")==0) {
|
|
name = name.mid(_clsName.size()+1);
|
|
completePopup->addItem(name, name);
|
|
} else if (_clsName.size() == 0) {
|
|
completePopup->addItem(name, "\\"+name);
|
|
}
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->phpClassesComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
QString _text = nsName.size() > 0 ? nsName + "\\" + text : text;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)>=0 && k.indexOf(_text, 0, Qt::CaseInsensitive)!=0) {
|
|
QString name = QString::fromStdString(it.first);
|
|
if (_clsName.size() > 0 && name.indexOf(_clsName+"\\")==0) {
|
|
name = name.mid(_clsName.size()+1);
|
|
completePopup->addItem(name, name);
|
|
} else if (_clsName.size() == 0) {
|
|
completePopup->addItem(name, "\\"+name);
|
|
}
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
} else if (prevWord == "new") {
|
|
// php classes (with params)
|
|
for (auto & it : CW->phpClassesComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
QString _text = nsName.size() > 0 ? nsName + "\\" + text : text;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second), TOOLTIP_DELIMITER);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->phpClassesComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
QString _text = nsName.size() > 0 ? nsName + "\\" + text : text;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)>=0 && k.indexOf(_text, 0, Qt::CaseInsensitive)!=0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second), TOOLTIP_DELIMITER);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
} else if (prevChar == ":" && prevPrevChar == ":" && prevWord.size() > 0 && prevWord[0] != "$") {
|
|
// detect class name
|
|
QString _clsName;
|
|
if ((prevWord.toLower() == "self" || prevWord.toLower() == "static") && clsName.size() > 0) {
|
|
_clsName = nsName.size() > 0 ? nsName + "\\" + clsName : clsName;
|
|
} else if (prevWord.toLower() == "parent" && clsName.size() > 0) {
|
|
QString ns = "";
|
|
if (nsName.size() > 0) ns = nsName + "\\";
|
|
for (int i=0; i<parseResultPHP.classes.size(); i++) {
|
|
ParsePHP::ParseResultClass _cls = parseResultPHP.classes.at(i);
|
|
if (_cls.name == "\\"+ns+clsName) {
|
|
QString parentClass = _cls.parent;
|
|
if (parentClass.size() > 0 && parentClass.at(0) == "\\") parentClass = parentClass.mid(1);
|
|
if (parentClass.size() > 0) {
|
|
_clsName = parentClass;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
_clsName= completeClassNamePHPAtCursor(curs, prevWord, nsName);
|
|
}
|
|
// php class consts
|
|
for (auto & it : CW->phpClassConstsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(_clsName+"::"+text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classConstComplete = getFixedCompleteClassConstName(QString::fromStdString(it.first));
|
|
completePopup->addItem(classConstComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// php class methods
|
|
for (auto & it : CW->phpClassMethodsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(_clsName+"::"+text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classMethodComplete = getFixedCompleteClassMethodName(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
completePopup->addItem(classMethodComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
} else if (prevChar == "?" && text == "php") {
|
|
// do not detect php tag
|
|
completeDetectedPHP = true;
|
|
}
|
|
} else if (text[0] == "_" && prevWord == "function") {
|
|
// php magic methods
|
|
for (auto & it : CW->phpMagicComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
} else if (text[0] == "$") {
|
|
std::unordered_map<std::string, std::string> vars;
|
|
std::unordered_map<std::string, std::string>::iterator varsIterator;
|
|
bool isSelf = false, isClass = false;
|
|
if (prevChar == ":" && prevPrevChar == ":") {
|
|
isClass = true;
|
|
}
|
|
if (isClass && (prevWord.toLower() == "self" || prevWord.toLower() == "static")) {
|
|
isSelf = true;
|
|
}
|
|
// parsed vars
|
|
if (clsName != "anonymous class" && funcName != "anonymous function" && !isClass) {
|
|
QString ns = "\\";
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
QString _clsName = clsName.size() > 0 ? ns + clsName : "";
|
|
QString _funcName = funcName;
|
|
if (_clsName.size() == 0 && _funcName.size() > 0) _funcName = ns + _funcName;
|
|
for (int i=parseResultPHP.variables.size()-1; i>=0; i--) {
|
|
ParsePHP::ParseResultVariable _variable = parseResultPHP.variables.at(i);
|
|
QString k = _variable.name;
|
|
if (_variable.clsName != _clsName || _variable.funcName != _funcName) continue;
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
varsIterator = vars.find(k.toStdString());
|
|
if (varsIterator == vars.end()) {
|
|
vars[k.toStdString()] = k.toStdString();
|
|
completePopup->addItem(k, k);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
}
|
|
// highlighted vars
|
|
if (!isClass && completePopup->count() < completePopup->limit()) {
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(curs.block().userData());
|
|
if (blockData != nullptr && blockData->varsChainPHP.size()>0 && (blockData->varsChainPHP.indexOf(text, 0, Qt::CaseInsensitive)==0 || blockData->varsChainPHP.indexOf(","+text, 0, Qt::CaseInsensitive)>0)) {
|
|
QStringList varsList = blockData->varsChainPHP.split(",");
|
|
for (QString k : varsList) {
|
|
if (k == text) continue; // need this
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
varsIterator = vars.find(k.toStdString());
|
|
if (varsIterator == vars.end()) {
|
|
vars[k.toStdString()] = k.toStdString();
|
|
completePopup->addItem(k, k);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// php self::$var
|
|
if (clsName.size() > 0 && clsName != "anonymous class" && funcName != "anonymous function" && isSelf && completePopup->count() < completePopup->limit()) {
|
|
QString ns = "\\";
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
QStringList vars = highlight->getKnownVars(ns + clsName, "");
|
|
for (int i=vars.size()-1; i>=0; i--) {
|
|
QString k = vars.at(i);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(k, k);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
}
|
|
// php globals
|
|
if (!isClass && completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->phpGlobalsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
}
|
|
// class props
|
|
if (isClass && !isSelf && prevWord.size() > 0 && prevWord[0] != "$" && completePopup->count() < completePopup->limit()) {
|
|
// detect class name
|
|
QString _clsName;
|
|
if (prevWord.toLower() == "parent" && clsName.size() > 0) {
|
|
QString ns = "";
|
|
if (nsName.size() > 0) ns = nsName + "\\";
|
|
for (int i=0; i<parseResultPHP.classes.size(); i++) {
|
|
ParsePHP::ParseResultClass _cls = parseResultPHP.classes.at(i);
|
|
if (_cls.name == "\\"+ns+clsName) {
|
|
QString parentClass = _cls.parent;
|
|
if (parentClass.size() > 0 && parentClass.at(0) == "\\") parentClass = parentClass.mid(1);
|
|
if (parentClass.size() > 0) {
|
|
_clsName = parentClass;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
_clsName= completeClassNamePHPAtCursor(curs, prevWord, nsName);
|
|
}
|
|
// php class vars
|
|
for (auto & it : CW->phpClassPropsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(_clsName+"::"+text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// do not detect if previous word is known keyword
|
|
completeDetectedPHP = true;
|
|
}
|
|
// last attempt
|
|
if (!completeDetectedPHP) {
|
|
// detect type
|
|
}
|
|
}
|
|
|
|
void Editor::detectCompleteTextPHPObjectContext(QString text, int cursorTextPos, QChar prevChar, QChar prevPrevChar, QString prevWord, QTextCursor curs)
|
|
{
|
|
if (prevChar != ">" || prevPrevChar != "-" || text[0] == "$") return;
|
|
bool completeDetectedPHP = false;
|
|
QTextCursor cursor = textCursor();
|
|
QTextBlock block = cursor.block();
|
|
QString nsName = highlight->findNsPHPAtCursor(& block, cursorTextPos);
|
|
QString clsName = highlight->findClsPHPAtCursor(& block, cursorTextPos);
|
|
QString funcName = highlight->findFuncPHPAtCursor(& block, cursorTextPos);
|
|
bool isThis = false;
|
|
if (prevWord == "$this") {
|
|
isThis = true;
|
|
}
|
|
// php $this->
|
|
if (clsName.size() > 0 && clsName != "anonymous class" && funcName != "anonymous function" && isThis) {
|
|
QString ns = "\\";
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
// $this->var
|
|
QStringList vars = highlight->getKnownVars(ns + clsName, "");
|
|
for (int i=vars.size()-1; i>=0; i--) {
|
|
QString k = vars.at(i);
|
|
if (k.size() < 2) continue;
|
|
k = k.mid(1);
|
|
//if (k == text) continue;
|
|
if (k.indexOf(text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(k, k);
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// $this->method()
|
|
QString _text = ns + clsName + "::" + text;
|
|
for (auto & it : CW->phpClassMethodsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classMethodComplete = getFixedCompleteClassMethodName(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
completePopup->addItem(classMethodComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// $this->prop
|
|
QString _text = ns + clsName + "::$" + text;
|
|
for (auto & it : CW->phpClassPropsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
} else if (prevWord.size() > 0 && prevWord[0] == "$") {
|
|
QChar _prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QChar _prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QString _prevWord = findPrevWordNonSpaceAtCursor(curs, MODE_PHP);
|
|
if (_prevChar != ":" || _prevPrevChar != ":" || (_prevWord.toLower() != "self" && _prevWord.toLower() != "static")) {
|
|
// $object->
|
|
if (clsName != "anonymous class" && funcName != "anonymous function") {
|
|
QString ns = "\\";
|
|
QString _clsName = clsName;
|
|
QString _funcName = funcName;
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
if (_clsName.size() > 0) _clsName = ns + _clsName;
|
|
else if (_funcName.size() > 0) _funcName = ns + _funcName;
|
|
ParsePHP::ParseResultVariable variable;
|
|
for (int i=0; i<parseResultPHP.variables.size(); i++) {
|
|
ParsePHP::ParseResultVariable _variable = parseResultPHP.variables.at(i);
|
|
if (_variable.name == prevWord && _variable.clsName == _clsName && _variable.funcName == _funcName) {
|
|
variable = _variable;
|
|
break;
|
|
}
|
|
}
|
|
if (variable.name == prevWord && variable.type.size() > 0) {
|
|
// class methods
|
|
QString _text = variable.type + "::" + text;
|
|
for (auto & it : CW->phpClassMethodsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classMethodComplete = getFixedCompleteClassMethodName(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
completePopup->addItem(classMethodComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// class props
|
|
QString _text = variable.type + "::$" + text;
|
|
for (auto & it : CW->phpClassPropsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
}
|
|
}
|
|
} else if (_prevChar == ":" && _prevPrevChar == ":" && (_prevWord.toLower() == "self" || _prevWord.toLower() == "static") && clsName.size() > 0 && clsName != "anonymous class" && funcName != "anonymous function") {
|
|
// self::$object->
|
|
QString ns = "\\";
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
QString _clsName = ns + clsName;
|
|
ParsePHP::ParseResultVariable variable;
|
|
for (int i=0; i<parseResultPHP.variables.size(); i++) {
|
|
ParsePHP::ParseResultVariable _variable = parseResultPHP.variables.at(i);
|
|
if (_variable.name == prevWord && _variable.clsName == _clsName && _variable.funcName.size() == 0) {
|
|
variable = _variable;
|
|
break;
|
|
}
|
|
}
|
|
if (variable.name == prevWord && variable.type.size() > 0) {
|
|
// class methods
|
|
QString _text = variable.type + "::" + text;
|
|
for (auto & it : CW->phpClassMethodsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classMethodComplete = getFixedCompleteClassMethodName(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
completePopup->addItem(classMethodComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// class props
|
|
QString _text = variable.type + "::$" + text;
|
|
for (auto & it : CW->phpClassPropsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
}
|
|
}
|
|
} else if (prevWord.size() > 0 && prevWord[0] != "$") {
|
|
QChar _prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QChar _prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QString _prevWord = findPrevWordNonSpaceAtCursor(curs, MODE_PHP);
|
|
if (_prevChar == ">" && _prevPrevChar == "-" && _prevWord == "$this" && clsName.size() > 0 && clsName != "anonymous class" && funcName != "anonymous function") {
|
|
// $this->object->
|
|
QString ns = "\\";
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
QString _clsName = ns + clsName;
|
|
ParsePHP::ParseResultVariable variable;
|
|
for (int i=0; i<parseResultPHP.variables.size(); i++) {
|
|
ParsePHP::ParseResultVariable _variable = parseResultPHP.variables.at(i);
|
|
if (_variable.name == "$"+prevWord && _variable.clsName == _clsName && _variable.funcName.size() == 0) {
|
|
variable = _variable;
|
|
break;
|
|
}
|
|
}
|
|
if (variable.name == "$"+prevWord && variable.type.size() > 0) {
|
|
// class methods
|
|
QString _text = variable.type + "::" + text;
|
|
for (auto & it : CW->phpClassMethodsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classMethodComplete = getFixedCompleteClassMethodName(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
completePopup->addItem(classMethodComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// class props
|
|
QString _text = variable.type + "::$" + text;
|
|
for (auto & it : CW->phpClassPropsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
completeDetectedPHP = true;
|
|
}
|
|
}
|
|
}
|
|
// last attempt
|
|
if (!completeDetectedPHP) {
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
if (cursorTextPos > 0) curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
// detect type
|
|
QString type = detectCompleteTypeAtCursorPHP(curs, nsName, clsName, funcName);
|
|
if (type.size() > 0) {
|
|
if (type[0] != "\\") type = "\\" + type;
|
|
// class methods
|
|
QString _text = type + "::" + text;
|
|
for (auto & it : CW->phpClassMethodsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classMethodComplete = getFixedCompleteClassMethodName(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
completePopup->addItem(classMethodComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// class props
|
|
QString _text = type + "::$" + text;
|
|
for (auto & it : CW->phpClassPropsComplete) {
|
|
QString k = "\\"+QString::fromStdString(it.first);
|
|
//if (k == _text) continue;
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QString Editor::getFixedCompleteClassMethodName(QString clsMethodComplete, QString params)
|
|
{
|
|
if (clsMethodComplete.indexOf(":") <= 0) return clsMethodComplete;
|
|
QString cls = clsMethodComplete.mid(0, clsMethodComplete.indexOf(":"));
|
|
QString func = clsMethodComplete.mid(cls.size()+2);
|
|
CW->phpClassParentsIterator = CW->phpClassParents.find(cls.toStdString());
|
|
if (CW->phpClassParentsIterator != CW->phpClassParents.end()) {
|
|
QStringList parentsList = QString::fromStdString(CW->phpClassParentsIterator->second).split(",");
|
|
for (QString _cls: parentsList) {
|
|
QString _clsMethod = _cls + "::" + func;
|
|
std::map<std::string, std::string>::iterator it = CW->phpClassMethodsComplete.find(_clsMethod.toStdString());
|
|
if (it != CW->phpClassMethodsComplete.end() && QString::fromStdString(it->second) == params) {
|
|
cls = _cls;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cls + "::" + func;
|
|
}
|
|
|
|
QString Editor::getFixedCompleteClassConstName(QString clsConstComplete)
|
|
{
|
|
if (clsConstComplete.indexOf(":") <= 0) return clsConstComplete;
|
|
QString cls = clsConstComplete.mid(0, clsConstComplete.indexOf(":"));
|
|
QString cons = clsConstComplete.mid(cls.size()+2);
|
|
CW->phpClassParentsIterator = CW->phpClassParents.find(cls.toStdString());
|
|
if (CW->phpClassParentsIterator != CW->phpClassParents.end()) {
|
|
QStringList parentsList = QString::fromStdString(CW->phpClassParentsIterator->second).split(",");
|
|
for (QString _cls: parentsList) {
|
|
QString _clsConst = _cls + "::" + cons;
|
|
std::map<std::string, std::string>::iterator it = CW->phpClassConstsComplete.find(_clsConst.toStdString());
|
|
if (it != CW->phpClassConstsComplete.end()) {
|
|
cls = _cls;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cls + "::" + cons;
|
|
}
|
|
|
|
void Editor::detectParsOpenAtCursor(QTextCursor & curs)
|
|
{
|
|
int pars = 0;
|
|
while(curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor)) {
|
|
QString blockText = curs.block().text();
|
|
int pos = curs.positionInBlock();
|
|
if (pos >= blockText.size()) continue;
|
|
QChar prevChar = blockText[pos];
|
|
if (prevChar == ")") {
|
|
pars++;
|
|
} else if (prevChar == "(") {
|
|
pars--;
|
|
}
|
|
if (pars == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::detectParsCloseAtCursor(QTextCursor & curs)
|
|
{
|
|
int pars = 0;
|
|
while(curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) {
|
|
QString blockText = curs.block().text();
|
|
int pos = curs.positionInBlock();
|
|
if (pos >= blockText.size()) continue;
|
|
QChar nextChar = blockText[pos];
|
|
if (nextChar == "(") {
|
|
pars++;
|
|
} else if (nextChar == ")") {
|
|
pars--;
|
|
}
|
|
if (pars == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
QString Editor::detectCompleteTypeAtCursorPHP(QTextCursor & curs, QString nsName, QString clsName, QString funcName)
|
|
{
|
|
int cursorTextBlockNumber = curs.block().blockNumber();
|
|
int cursorTextPos = curs.positionInBlock();
|
|
QString prevType = "";
|
|
if (!parsePHPEnabled) return prevType;
|
|
// search begin of statement
|
|
QChar prevChar = '\0';
|
|
QChar prevPrevChar = '\0';
|
|
QString prevWord = "", keyW = "";
|
|
int keyWPos = -1;
|
|
do {
|
|
prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
prevWord = findPrevWordNonSpaceAtCursor(curs, MODE_PHP);
|
|
if (prevWord.size() > 0) {
|
|
QTextBlock block = curs.block();
|
|
int pos = curs.positionInBlock();
|
|
int state = highlight->findStateAtCursor(&block, pos);
|
|
if (state != STATE_NONE) break;
|
|
}
|
|
if (prevWord.toLower() == "return" || prevWord.toLower() == "else" || prevWord.toLower() == "echo") break;
|
|
/*
|
|
HW->phpwordsIterator = HW->phpwords.find(prevWord.toLower().toStdString());
|
|
if (HW->phpwordsIterator != HW->phpwords.end()) break;
|
|
*/
|
|
if (prevChar == ")") {
|
|
detectParsOpenAtCursor(curs);
|
|
prevWord = findPrevWordNonSpaceAtCursor(curs, MODE_PHP);
|
|
if (prevWord.toLower() == "if" || prevWord.toLower() == "echo") break;
|
|
/*
|
|
HW->phpwordsIterator = HW->phpwords.find(prevWord.toLower().toStdString());
|
|
if (HW->phpwordsIterator != HW->phpwords.end()) break;
|
|
*/
|
|
if (prevWord.size() > 0) {
|
|
keyW = prevWord;
|
|
keyWPos = curs.position();
|
|
}
|
|
prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
prevWord = findPrevWordNonSpaceAtCursor(curs, MODE_PHP);
|
|
}
|
|
if (prevWord.size() > 0) {
|
|
keyW = prevWord;
|
|
keyWPos = curs.position();
|
|
}
|
|
if (curs.positionInBlock() > 0) {
|
|
QString blockText = curs.block().text();
|
|
if (blockText[curs.positionInBlock()-1] == "\\") {
|
|
prevType = completeClassNamePHPAtCursor(curs, prevWord, nsName);
|
|
keyWPos += keyW.size();
|
|
keyW = "";
|
|
break;
|
|
}
|
|
}
|
|
} while ((prevChar == ">" && prevPrevChar == "-") || (prevChar == ":" && prevPrevChar == ":"));
|
|
if (keyW.size() > 0) keyWPos += keyW.size();
|
|
if (keyW.size() > 0 && keyW[0] == "$") {
|
|
if (keyW == "$this") {
|
|
if (clsName != "anonymous class") {
|
|
QString ns = "";
|
|
if (nsName.size() > 0) ns = nsName + "\\";
|
|
prevType = ns + clsName;
|
|
}
|
|
} else if (funcName != "anonymous function") {
|
|
QString ns = "\\";
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
QString _clsName = clsName, _funcName = funcName;
|
|
if (clsName.size() > 0) _clsName = ns + clsName;
|
|
else if (funcName.size() > 0) _funcName = ns + funcName;
|
|
for (int i=0; i<parseResultPHP.variables.size(); i++) {
|
|
ParsePHP::ParseResultVariable _variable = parseResultPHP.variables.at(i);
|
|
if (_variable.name == keyW && _variable.clsName == _clsName && _variable.funcName == _funcName) {
|
|
// detect variable type
|
|
if (_variable.type.size() == 0 && _variable.line > 0 && _variable.line-1 < cursorTextBlockNumber) {
|
|
QTextCursor _curs = textCursor();
|
|
_curs.movePosition(QTextCursor::Start);
|
|
if (_variable.line > 1) _curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, _variable.line-1);
|
|
QString _blockText = _curs.block().text();
|
|
QRegularExpression r = QRegularExpression(QRegularExpression::escape(_variable.name)+"[\\s]*[=](.+?[)])[\\s]*[;]");
|
|
QRegularExpressionMatch m = r.match(_blockText);
|
|
QString _varType = "";
|
|
if (m.capturedStart(1) > 0) {
|
|
int _cursorTextPos = m.capturedStart(1)+m.capturedLength(1);
|
|
_curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, _cursorTextPos);
|
|
_varType = detectCompleteTypeAtCursorPHP(_curs, nsName, clsName, funcName);
|
|
} else {
|
|
QRegularExpression rr = QRegularExpression(QRegularExpression::escape(_variable.name)+"[\\s]*[=](.+?[)])");
|
|
QRegularExpressionMatch mm = rr.match(_blockText);
|
|
if (mm.capturedStart(1) > 0) {
|
|
int cp = -1;
|
|
while (_curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor)) {
|
|
_blockText = _curs.block().text();
|
|
cp = _blockText.indexOf(";");
|
|
if (cp >= 0) break;
|
|
}
|
|
if (cp >= 0) {
|
|
_curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cp);
|
|
_varType = detectCompleteTypeAtCursorPHP(_curs, nsName, clsName, funcName);
|
|
}
|
|
}
|
|
}
|
|
if (_varType.size() == 0) _varType = "mixed";
|
|
_variable.type = "\\"+_varType;
|
|
parseResultPHP.variables.replace(i, _variable);
|
|
}
|
|
prevType = _variable.type;
|
|
break;
|
|
}
|
|
}
|
|
// detect global variable
|
|
if (prevType.size() == 0 && funcName.size() > 0) {
|
|
QString varName = "";
|
|
QString ns = "\\";
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
QString _clsName = clsName, _funcName = funcName;
|
|
if (clsName.size() > 0) _clsName = ns + clsName;
|
|
else if (funcName.size() > 0) _funcName = ns + funcName;
|
|
for (int i=0; i<parseResultPHP.functions.size(); i++) {
|
|
ParsePHP::ParseResultFunction _func = parseResultPHP.functions.at(i);
|
|
if (_func.name == _funcName && _func.clsName == _clsName && _func.line > 0 && _func.line-1 != cursorTextBlockNumber) {
|
|
QTextCursor _curs = textCursor();
|
|
_curs.movePosition(QTextCursor::Start);
|
|
if (_func.line > 1) _curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, _func.line-1);
|
|
_curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
|
|
QString _blockText = _curs.block().text();
|
|
while(_blockText.trimmed().size()==0 || _blockText.trimmed()=="{") {
|
|
_curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
|
|
_blockText = _curs.block().text();
|
|
}
|
|
QRegularExpression r = QRegularExpression("global[\\s]+(.+?)[;]");
|
|
QRegularExpressionMatch m = r.match(_blockText);
|
|
if (m.capturedStart(1) > 0) {
|
|
QStringList varList = m.captured(1).split(",");
|
|
for (int i=0; i<varList.size(); i++) {
|
|
QString _varName = varList.at(i);
|
|
_varName = _varName.trimmed();
|
|
if (_varName == keyW) {
|
|
varName = _varName;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (varName.size() > 0) {
|
|
for (int i=0; i<parseResultPHP.variables.size(); i++) {
|
|
ParsePHP::ParseResultVariable _variable = parseResultPHP.variables.at(i);
|
|
if (_variable.name == varName && _variable.clsName.size() == 0 && _variable.funcName.size() == 0) {
|
|
prevType = _variable.type;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (keyW.size() > 0 && keyW[0] != "$" && keyW.toLower() != "self" && keyW.toLower() != "static" && keyW.toLower() != "parent") {
|
|
if (keyW.indexOf("\\") < 0) {
|
|
CW->phpFunctionTypesIterator = CW->phpFunctionTypes.find(keyW.toStdString());
|
|
if (CW->phpFunctionTypesIterator != CW->phpFunctionTypes.end()) {
|
|
prevType = QString::fromStdString(CW->phpFunctionTypesIterator->second);
|
|
} else {
|
|
prevType = completeClassNamePHPAtCursor(curs, keyW, nsName);
|
|
}
|
|
} else {
|
|
prevType = completeClassNamePHPAtCursor(curs, keyW, nsName);
|
|
}
|
|
} else if (keyW.size() > 0 && (keyW.toLower() == "self" || keyW.toLower() == "static")) {
|
|
if (clsName != "anonymous class") {
|
|
QString ns = "";
|
|
if (nsName.size() > 0) ns = nsName + "\\";
|
|
prevType = ns + clsName;
|
|
}
|
|
} else if (keyW.size() > 0 && keyW.toLower() == "parent") {
|
|
if (clsName != "anonymous class") {
|
|
QString ns = "";
|
|
if (nsName.size() > 0) ns = nsName + "\\";
|
|
for (int i=0; i<parseResultPHP.classes.size(); i++) {
|
|
ParsePHP::ParseResultClass _cls = parseResultPHP.classes.at(i);
|
|
if (_cls.name == "\\"+ns+clsName) {
|
|
QString parentClass = _cls.parent;
|
|
if (parentClass.size() > 0 && parentClass.at(0) == "\\") parentClass = parentClass.mid(1);
|
|
if (parentClass.size() > 0) {
|
|
prevType = parentClass;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (prevType.size() > 0 && prevType[0] == "\\") prevType = prevType.mid(1);
|
|
if (prevType.size() > 0) {
|
|
// search end of statement
|
|
if (keyWPos >= 0) curs.setPosition(keyWPos);
|
|
QChar nextChar = findNextCharNonSpaceAtCursos(curs);
|
|
QChar nextNextChar = findNextCharNonSpaceAtCursos(curs);
|
|
QString nextWord = "";
|
|
do {
|
|
nextWord = findNextWordNonSpaceAtCursor(curs, MODE_PHP);
|
|
nextChar = findNextCharNonSpaceAtCursos(curs);
|
|
nextNextChar = findNextCharNonSpaceAtCursos(curs);
|
|
if (nextWord.size() == 0) break;
|
|
if ((curs.block().blockNumber() == cursorTextBlockNumber && curs.positionInBlock() >= cursorTextPos) || curs.block().blockNumber() > cursorTextBlockNumber) {
|
|
break;
|
|
}
|
|
if (nextChar == "(") {
|
|
// search function return type
|
|
QString _funcName = prevType+"::"+nextWord;
|
|
if (_funcName[0] == "\\") _funcName = _funcName.mid(1);
|
|
CW->phpClassMethodTypesIterator = CW->phpClassMethodTypes.find(_funcName.toStdString());
|
|
if (CW->phpClassMethodTypesIterator != CW->phpClassMethodTypes.end()) {
|
|
prevType = QString::fromStdString(CW->phpClassMethodTypesIterator->second);
|
|
} else {
|
|
CW->phpClassParentsIterator = CW->phpClassParents.find(prevType.toStdString());
|
|
if (CW->phpClassParentsIterator != CW->phpClassParents.end()) {
|
|
QString parent = QString::fromStdString(CW->phpClassParentsIterator->second);
|
|
if (parent.size() > 0) {
|
|
QStringList parentList = parent.split(",");
|
|
for (int i=0; i<parentList.size(); i++) {
|
|
QString parentClass = parentList.at(i);
|
|
_funcName = parentClass + "::" + nextWord;
|
|
CW->phpClassMethodTypesIterator = CW->phpClassMethodTypes.find(_funcName.toStdString());
|
|
if (CW->phpClassMethodTypesIterator != CW->phpClassMethodTypes.end()) {
|
|
prevType = QString::fromStdString(CW->phpClassMethodTypesIterator->second);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
detectParsCloseAtCursor(curs);
|
|
nextChar = findNextCharNonSpaceAtCursos(curs);
|
|
nextNextChar = findNextCharNonSpaceAtCursos(curs);
|
|
} else {
|
|
// search variable type
|
|
if (nextWord[0] != "$") nextWord = "$" + nextWord;
|
|
QString _clsName = "\\" + prevType;
|
|
for (int i=0; i<parseResultPHP.variables.size(); i++) {
|
|
ParsePHP::ParseResultVariable _variable = parseResultPHP.variables.at(i);
|
|
if (_variable.name == nextWord && _variable.clsName == _clsName && _variable.funcName.size() == 0) {
|
|
prevType = _variable.type;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (prevType.size() == 0) break;
|
|
if ((curs.block().blockNumber() == cursorTextBlockNumber && curs.positionInBlock() >= cursorTextPos) || curs.block().blockNumber() > cursorTextBlockNumber) {
|
|
break;
|
|
}
|
|
} while ((nextChar == "-" && nextNextChar == ">") || (nextChar == ":" && nextNextChar == ":"));
|
|
}
|
|
return prevType;
|
|
}
|
|
|
|
void Editor::detectCompleteTextPHPNotFoundContext(QString text, QChar prevChar, QChar prevPrevChar)
|
|
{
|
|
if (prevChar == ">" && prevPrevChar == "-") {
|
|
// class methods
|
|
QTextCursor curs = textCursor();
|
|
if (text.size() > 0) curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, text.size());
|
|
QTextBlock block = curs.block();
|
|
int pos = curs.positionInBlock();
|
|
QString nsName = highlight->findNsPHPAtCursor(& block, pos);
|
|
QString clsName = highlight->findClsPHPAtCursor(& block, pos);
|
|
QString funcName = highlight->findFuncPHPAtCursor(& block, pos);
|
|
QString prevType = detectCompleteTypeAtCursorPHP(curs, nsName, clsName, funcName);
|
|
std::unordered_map<std::string, std::string> addedClassMethods;
|
|
std::unordered_map<std::string, std::string>::iterator addedClassMethodsIterator;
|
|
if (prevType.size() > 0) {
|
|
QString _text = prevType + "::" + text;
|
|
for (auto & it : CW->phpClassMethodsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classMethodComplete = getFixedCompleteClassMethodName(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
addedClassMethodsIterator = addedClassMethods.find(classMethodComplete.toStdString());
|
|
if (addedClassMethodsIterator != addedClassMethods.end()) continue;
|
|
addedClassMethods[classMethodComplete.toStdString()] = classMethodComplete.toStdString();
|
|
completePopup->addItem(classMethodComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// class props
|
|
QString _text = prevType + "::$" + text;
|
|
for (auto & it : CW->phpClassPropsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (text.size() > 0) {
|
|
QString _text = "::" + text;
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
for (auto & it : CW->phpClassMethodsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)>0) {
|
|
if (prevType.size() > 0 && k.indexOf(prevType+"::", 0, Qt::CaseInsensitive)==0) continue;
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString _clsName = k.mid(0, k.indexOf(":"));
|
|
QString classMethodComplete = getFixedCompleteClassMethodName(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
addedClassMethodsIterator = addedClassMethods.find(classMethodComplete.toStdString());
|
|
if (addedClassMethodsIterator != addedClassMethods.end()) continue;
|
|
addedClassMethods[classMethodComplete.toStdString()] = classMethodComplete.toStdString();
|
|
completePopup->addItem(classMethodComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// class props
|
|
QString _text = "::$" + text;
|
|
for (auto & it : CW->phpClassPropsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)>0) {
|
|
if (prevType.size() > 0 && k.indexOf(prevType+"::", 0, Qt::CaseInsensitive)==0) continue;
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (prevChar == ":" && prevPrevChar == ":" && text.size() > 0) {
|
|
// class consts
|
|
QString _text = text + "::";
|
|
for (auto & it : CW->phpClassConstsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classConstComplete = getFixedCompleteClassConstName(QString::fromStdString(it.first));
|
|
completePopup->addItem(classConstComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// class methods
|
|
QString _text = text + "::";
|
|
for (auto & it : CW->phpClassMethodsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
//completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
QString classMethodComplete = getFixedCompleteClassMethodName(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
completePopup->addItem(classMethodComplete, QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
if (completePopup->count() < completePopup->limit()) {
|
|
// class props
|
|
QString _text = text+"::$";
|
|
for (auto & it : CW->phpClassPropsComplete) {
|
|
QString k = QString::fromStdString(it.first);
|
|
if (k.indexOf(_text, 0, Qt::CaseInsensitive)==0) {
|
|
completePopup->addItem(QString::fromStdString(it.first), QString::fromStdString(it.second));
|
|
if (completePopup->count() >= completePopup->limit()) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::detectCompleteTextPHP(QString text, int cursorTextPos, QChar cursorTextPrevChar)
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
if (cursorTextPos > 0) curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QChar prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QChar prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QString prevWord = findPrevWordNonSpaceAtCursor(curs, MODE_PHP);
|
|
if (prevChar != ">" || prevPrevChar != "-") {
|
|
detectCompleteTextPHPGlobalContext(text, cursorTextPos, prevChar, prevPrevChar, prevWord, curs, cursorTextPrevChar);
|
|
} else if (prevChar == ">" && prevPrevChar == "-" && text[0] != "$") {
|
|
detectCompleteTextPHPObjectContext(text, cursorTextPos, prevChar, prevPrevChar, prevWord, curs);
|
|
}
|
|
}
|
|
|
|
void Editor::detectCompleteText(QString text, QChar cursorTextPrevChar, int cursorTextPos, std::string mode, int state)
|
|
{
|
|
int min = 2;
|
|
if (mode == MODE_HTML) min = 1;
|
|
if (text.size() < min || isMultiSelectMode) return;
|
|
completePopup->clearItems();
|
|
|
|
if (mode == MODE_HTML) {
|
|
detectCompleteTextHTML(text, cursorTextPrevChar, state);
|
|
} else if (mode == MODE_CSS) {
|
|
detectCompleteTextCSS(text, cursorTextPrevChar);
|
|
} else if (mode == MODE_JS) {
|
|
detectCompleteTextJS(text, cursorTextPos, cursorTextPrevChar, highlight->getJsExtMode());
|
|
} else if (mode == MODE_PHP) {
|
|
detectCompleteTextPHP(text, cursorTextPos, cursorTextPrevChar);
|
|
}
|
|
|
|
if (completePopup->count()>0) {
|
|
completePopup->setTextStartPos(cursorTextPos);
|
|
showCompletePopup();
|
|
}
|
|
}
|
|
|
|
void Editor::detectCompleteTextRequest(QString text, int cursorTextPos, QChar prevChar, QChar prevPrevChar, std::string mode)
|
|
{
|
|
completePopup->clearItems();
|
|
|
|
if (mode == MODE_PHP) {
|
|
detectCompleteTextPHPNotFoundContext(text, prevChar, prevPrevChar);
|
|
}
|
|
|
|
if (completePopup->count()>0) {
|
|
completePopup->setTextStartPos(cursorTextPos);
|
|
showCompletePopup();
|
|
}
|
|
}
|
|
|
|
void Editor::completePopupSelected(QString text, QString data)
|
|
{
|
|
if (text.size() == 0) return;
|
|
QString origText = text;
|
|
int cursorTextPos = completePopup->getTextStartPos();
|
|
QTextCursor curs = textCursor();
|
|
QTextBlock block = curs.block();
|
|
int pos = curs.positionInBlock();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
int startPos = curs.position();
|
|
std::string mode = highlight->findModeAtCursor(& block, pos);
|
|
int state = highlight->findStateAtCursor(& block, pos);
|
|
QChar nextChar = '\0';
|
|
if (pos < total) nextChar = blockText[pos];
|
|
int moveCursorBack = 0;
|
|
bool isSnippet = false;
|
|
int setSelectStartFromEnd = 0;
|
|
int setSelectLength = 0;
|
|
int setMultiSelectStartFromEnd = 0;
|
|
int setMultiSelectLength = 0;
|
|
int isMultiSelectSnippet = false;
|
|
if (cursorTextPos >= 0 && cursorTextPos <= pos) {
|
|
if (text.indexOf(SNIPPET_PREFIX) == 0) {
|
|
// indent
|
|
QString prefix = "";
|
|
QTextCursor cursor = textCursor();
|
|
QString blockText = cursor.block().text();
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
int pos = cursor.positionInBlock();
|
|
int total = blockText.size();
|
|
while(pos < total) {
|
|
if (!cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) break;
|
|
pos = cursor.positionInBlock();
|
|
QChar prevChar = blockText[pos-1];
|
|
if ((prevChar == " " || prevChar == "\t")) {
|
|
prefix += prevChar;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
QString indent = (tabType == "spaces") ? QString(" ").repeated(tabWidth) : "\t";
|
|
text = Snippets::parse(data, prefix, indent, moveCursorBack, setSelectStartFromEnd, setSelectLength, setMultiSelectStartFromEnd, setMultiSelectLength);
|
|
isSnippet = true;
|
|
} else if (mode == MODE_PHP) {
|
|
QTextCursor cursor = textCursor();
|
|
if (cursorTextPos < pos) {
|
|
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, pos - cursorTextPos);
|
|
}
|
|
QChar prevChar = findPrevCharNonSpaceAtCursos(cursor);
|
|
QChar prevPrevChar = findPrevCharNonSpaceAtCursos(cursor);
|
|
QString prevWord = findPrevWordNonSpaceAtCursor(cursor, mode);
|
|
if ((prevWord == "new" && prevChar != "\\") || (data == "\\"+text && prevChar != "\\")) {
|
|
// new class
|
|
QString nsName = highlight->findNsPHPAtCursor(& block, cursorTextPos);
|
|
//QString clsName = highlight->findClsPHPAtCursor(& block, pos);
|
|
//QString funcName = highlight->findFuncPHPAtCursor(& block, pos);
|
|
if (nsName.size() > 0) {
|
|
QString clsName = text;
|
|
int p = text.lastIndexOf("\\");
|
|
if (p > 0 && p < text.size()-1) {
|
|
clsName = text.mid(p+1);
|
|
}
|
|
int line = -1;
|
|
// update parse result
|
|
parseResultPHPChanged(false);
|
|
// find imports
|
|
ParsePHP::ParseResultNamespace ns;
|
|
ParsePHP::ParseResultImport imp;
|
|
for (int i=0; i<parseResultPHP.namespaces.size(); i++) {
|
|
ParsePHP::ParseResultNamespace _ns = parseResultPHP.namespaces.at(i);
|
|
if (_ns.name == nsName) {
|
|
ns = _ns;
|
|
line = _ns.line;
|
|
break;
|
|
}
|
|
}
|
|
for (int c=0; c<ns.importsIndexes.size(); c++) {
|
|
if (parseResultPHP.imports.size() <= ns.importsIndexes.at(c)) break;
|
|
ParsePHP::ParseResultImport _imp = parseResultPHP.imports.at(ns.importsIndexes.at(c));
|
|
line = _imp.line;
|
|
if (_imp.type == IMPORT_TYPE_CLASS && _imp.name == clsName) {
|
|
imp = _imp;
|
|
break;
|
|
}
|
|
}
|
|
// import class
|
|
if (ns.name == nsName && line > 0 && imp.name != clsName && text != nsName + "\\" + clsName) {
|
|
QString insertImportText = "use "+text+";\n";
|
|
QTextCursor cursor = textCursor();
|
|
cursor.movePosition(QTextCursor::Start);
|
|
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, line);
|
|
if (line == ns.line) {
|
|
QString blockText = cursor.block().text().trimmed();
|
|
if (blockText.size() == 0) {
|
|
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
|
|
insertImportText += "\n";
|
|
}
|
|
}
|
|
blockSignals(true);
|
|
cursor.beginEditBlock();
|
|
cursor.insertText(insertImportText);
|
|
cursor.endEditBlock();
|
|
blockSignals(false);
|
|
text = clsName;
|
|
emit showPopupText(tabIndex, insertImportText);
|
|
} else if (ns.name == nsName && line > 0 && imp.name == clsName && imp.path != "\\"+text) {
|
|
text = "\\" + text;
|
|
} else if (ns.name == nsName && line > 0) {
|
|
text = clsName;
|
|
}
|
|
}
|
|
} else if (prevChar == ">" && prevPrevChar == "-") {
|
|
// object method or prop
|
|
if (text.indexOf("::") > 0) {
|
|
text = text.mid(text.indexOf("::")+2);
|
|
if (text[0] == "$") text = text.mid(1);
|
|
}
|
|
} else if (prevChar == ":" && prevPrevChar == ":") {
|
|
if (text.indexOf("::") > 0) {
|
|
text = text.mid(text.indexOf("::")+2);
|
|
}
|
|
}
|
|
} else if (mode == MODE_HTML) {
|
|
QString tag = text;
|
|
CW->htmlTagsIterator = CW->htmlTags.find(tag.toLower().toStdString());
|
|
if (CW->htmlTagsIterator != CW->htmlTags.end()) {
|
|
QChar prevChar = '\0';
|
|
if (cursorTextPos - 1 >= 0) prevChar = blockText[cursorTextPos - 1];
|
|
if (prevChar == '<') {
|
|
text += "></"+tag+">";
|
|
moveCursorBack = tag.size() + 3;
|
|
} else if (prevChar == '/') {
|
|
text += ">";
|
|
}
|
|
} else if (state == STATE_TAG && text.toLower().indexOf("on") == 0) {
|
|
text += "=\"\"";
|
|
moveCursorBack = 1;
|
|
}
|
|
}
|
|
if (cursorTextPos < pos) {
|
|
if (isSnippet && cursorTextPos > 0) cursorTextPos--;
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, pos - cursorTextPos);
|
|
}
|
|
bool blockS = true;
|
|
if (mode != MODE_HTML && state != STATE_NONE) blockS = false;
|
|
if (mode == MODE_HTML && state != STATE_TAG) blockS = false;
|
|
if (mode == MODE_UNKNOWN) blockS = false;
|
|
if (blockS) blockSignals(true);
|
|
curs.beginEditBlock();
|
|
curs.insertText(text);
|
|
// show tooltip
|
|
QString tooltipText = "", tooltipOrigText = "";
|
|
QStringList tooltipList;
|
|
if (data.size() > 0 && data[0] == "(") {
|
|
data.replace("<", "<").replace(">", ">");
|
|
tooltipList = data.split(TOOLTIP_DELIMITER);
|
|
if (tooltipList.size() > 1) {
|
|
QString param = tooltipList.at(0);
|
|
data = param.trimmed() + TOOLTIP_PAGER_TPL.arg(1).arg(tooltipList.size());
|
|
}
|
|
}
|
|
if (data.size() > 0 && data[0] == "(" && !isSnippet) {
|
|
if (nextChar == '\0' || (nextChar == ')' && blockText.count("(") == blockText.count(")"))) {
|
|
curs.insertText("()");
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor);
|
|
} else if (nextChar != "(") {
|
|
curs.insertText("(");
|
|
} else {
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
|
|
}
|
|
if (data != "()") {
|
|
tooltipText = text + " " + data;
|
|
tooltipOrigText = origText + " " + data;
|
|
}
|
|
} else if (moveCursorBack > 0) {
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, moveCursorBack);
|
|
} else if (setSelectStartFromEnd > 0 && setSelectLength > 0 && setSelectLength <= setSelectStartFromEnd) {
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, setSelectStartFromEnd);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, setSelectLength);
|
|
} else if (setMultiSelectStartFromEnd > 0 && setMultiSelectLength > 0 && setMultiSelectLength <= setMultiSelectStartFromEnd) {
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, setMultiSelectStartFromEnd);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, setMultiSelectLength);
|
|
isMultiSelectSnippet = true;
|
|
}
|
|
curs.endEditBlock();
|
|
if (blockS) blockSignals(false);
|
|
setTextCursor(curs);
|
|
if (tooltipText.size() > 0) {
|
|
showTooltip(& curs, tooltipOrigText);
|
|
tooltipSavedText = tooltipText;
|
|
tooltipSavedList.clear();
|
|
for (int i=0; i<tooltipList.size(); i++) {
|
|
QString param = tooltipList.at(i);
|
|
tooltipSavedList.append(text + " " + param.replace("<", "<").replace(">", ">").trimmed() + TOOLTIP_PAGER_TPL.arg(i+1).arg(tooltipList.size()));
|
|
}
|
|
tooltipSavedPageOffset = 0;
|
|
tooltipSavedOrigName = origText;
|
|
tooltipSavedBlockNumber = curs.block().blockNumber();
|
|
}
|
|
if (isSnippet) {
|
|
QTextCursor cursor = textCursor();
|
|
cursor.setPosition(startPos);
|
|
QTextBlock block = cursor.block();
|
|
highlight->resetHighlightBlock(block);
|
|
highlight->highlightChanges(cursor);
|
|
if (isMultiSelectSnippet) {
|
|
multiSelectToggle();
|
|
}
|
|
}
|
|
}
|
|
hideCompletePopup();
|
|
}
|
|
|
|
void Editor::parseResultChanged()
|
|
{
|
|
std::string modeType = highlight->getModeType();
|
|
if (modeType == MODE_MIXED) {
|
|
parseResultPHPChanged();
|
|
} else if (modeType == MODE_JS) {
|
|
parseResultJSChanged();
|
|
} else if (modeType == MODE_CSS) {
|
|
parseResultCSSChanged();
|
|
}
|
|
}
|
|
|
|
void Editor::parseResultPHPChanged(bool async)
|
|
{
|
|
if (!parsePHPEnabled) return;
|
|
QTextCursor curs = textCursor();
|
|
QTextBlock block = curs.block();
|
|
int pos = curs.positionInBlock();
|
|
std::string mode = highlight->findModeAtCursor(& block, pos);
|
|
if (mode != MODE_PHP) return;
|
|
QString content = getContent();
|
|
if (!async) parseResultPHP = parserPHP.parse(content);
|
|
else emit parsePHP(getTabIndex(), content);
|
|
}
|
|
|
|
void Editor::parseResultJSChanged(bool async)
|
|
{
|
|
if (!parseJSEnabled) return;
|
|
QString content = getContent();
|
|
if (!async) parseResultJS = parserJS.parse(content);
|
|
else emit parseJS(getTabIndex(), content);
|
|
}
|
|
|
|
void Editor::parseResultCSSChanged(bool async)
|
|
{
|
|
if (!parseCSSEnabled) return;
|
|
QString content = getContent();
|
|
if (!async) parseResultCSS = parserCSS.parse(content);
|
|
else emit parseCSS(getTabIndex(), content);
|
|
}
|
|
|
|
void Editor::tooltip(int offset)
|
|
{
|
|
if (!focused) return;
|
|
QTextCursor cursor = textCursor();
|
|
QTextBlock block = cursor.block();
|
|
int cursPos = cursor.positionInBlock();
|
|
QString blockText = cursor.block().text();
|
|
if (highlight->isStateOpen(&block, cursPos)) return;
|
|
std::string mode = highlight->findModeAtCursor(&block, cursPos);
|
|
if (mode != MODE_PHP && mode != MODE_JS) return;
|
|
// show complete popup
|
|
QTextCursor curs = textCursor();
|
|
QString prevText = "";
|
|
QChar prevChar = '\0', prevPrevChar = '\0';
|
|
int cursorTextPos = cursPos;
|
|
if (cursPos > 1 && blockText[cursPos-1] == ":" && blockText[cursPos-2] == ":") {
|
|
prevChar = ':'; prevPrevChar = ':';
|
|
cursPos -= 2;
|
|
while (cursPos > 0) {
|
|
QChar _prevChar = blockText[cursPos-1];
|
|
if (!_prevChar.isSpace()) break;
|
|
cursPos--;
|
|
}
|
|
}
|
|
if (cursPos > 0 && cursor.selectedText().size()==0) {
|
|
// text till cursor
|
|
QChar cursorTextPrevChar = '\0';
|
|
for (int i=cursPos; i>0; i--) {
|
|
QString c = blockText.mid(i-1, 1);
|
|
cursorTextPrevChar = c[0];
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
cursorTextPos = i-1;
|
|
}
|
|
if ((prevText.size() > 0 && prevText[0] != "$") || prevText.size() == 0) {
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
if (cursorTextPos > 0) curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
if (prevChar == '\0' && prevPrevChar == '\0') {
|
|
prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
}
|
|
}
|
|
}
|
|
if ((prevChar == ">" && prevPrevChar == "-") || (prevChar == ":" && prevPrevChar == ":")) {
|
|
if (prevChar == ":" && prevPrevChar == ":") {
|
|
QString nsName = highlight->findNsPHPAtCursor(& block, cursPos);
|
|
QString clsName = highlight->findClsPHPAtCursor(& block, cursPos);
|
|
if ((prevText.toLower() == "self" || prevText.toLower() == "static") && clsName.size() > 0) {
|
|
prevText = nsName.size() > 0 ? nsName + "\\" + clsName : clsName;
|
|
} else if (prevText.toLower() == "parent" && clsName.size() > 0) {
|
|
QString ns = "";
|
|
if (nsName.size() > 0) ns = nsName + "\\";
|
|
for (int i=0; i<parseResultPHP.classes.size(); i++) {
|
|
ParsePHP::ParseResultClass _cls = parseResultPHP.classes.at(i);
|
|
if (_cls.name == "\\"+ns+clsName) {
|
|
QString parentClass = _cls.parent;
|
|
if (parentClass.size() > 0 && parentClass.at(0) == "\\") parentClass = parentClass.mid(1);
|
|
if (parentClass.size() > 0) {
|
|
prevText = parentClass;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
prevText = completeClassNamePHPAtCursor(curs, prevText, nsName);
|
|
}
|
|
cursorTextPos = cursor.positionInBlock();
|
|
}
|
|
detectCompleteTextRequest(prevText, cursorTextPos, prevChar, prevPrevChar, mode);
|
|
return;
|
|
}
|
|
// show tooltip
|
|
int tnOffset = 0, thCaptured = -1, tooltipStart = -1;
|
|
QString tooltipName = "";
|
|
do {
|
|
do {
|
|
QRegularExpressionMatch tnMatch = classNameExpr.match(blockText, tnOffset);
|
|
thCaptured = tnMatch.capturedStart();
|
|
int tnLength = tnMatch.capturedLength();
|
|
if (thCaptured >= 0) {
|
|
if (cursPos >= 0 && thCaptured >= cursPos) break;
|
|
if (tnMatch.captured(1) != "array") {
|
|
tooltipStart = thCaptured;
|
|
tooltipName = tnMatch.captured(1);
|
|
}
|
|
tnOffset = thCaptured + tnLength;
|
|
}
|
|
} while (thCaptured >= 0);
|
|
cursPos = -1;
|
|
tnOffset = 0;
|
|
} while(tooltipStart < 0 && cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor));
|
|
if (tooltipName.size()>0) {
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, tooltipStart);
|
|
int cursorP = cursor.position();
|
|
QChar prevChar = findPrevCharNonSpaceAtCursos(cursor);
|
|
QChar prevPrevChar = findPrevCharNonSpaceAtCursos(cursor);
|
|
if (tooltipName[0] == "\\") tooltipName = tooltipName.mid(1);
|
|
CW->tooltipsIteratorPHP = CW->tooltipsPHP.find(tooltipName.toStdString());
|
|
if (CW->tooltipsIteratorPHP != CW->tooltipsPHP.end() && (prevChar != ">" || prevPrevChar != "-") && (prevChar != ":" || prevPrevChar != ":") && prevChar != "$") {
|
|
QString fName = QString::fromStdString(CW->tooltipsIteratorPHP->first);
|
|
QString params = QString::fromStdString(CW->tooltipsIteratorPHP->second);
|
|
params.replace("<", "<").replace(">", ">");
|
|
if (tooltipSavedOrigName != fName || tooltipSavedPageOffset < 0 || offset > 0) {
|
|
tooltipSavedPageOffset = offset;
|
|
} else {
|
|
offset = tooltipSavedPageOffset;
|
|
}
|
|
QStringList paramsList;
|
|
if (params.size() > 0 && params[0] == "(") {
|
|
paramsList = params.split(TOOLTIP_DELIMITER);
|
|
if (offset < 0 || offset >= paramsList.size()) offset = 0;
|
|
if (paramsList.size() > 1) {
|
|
QString param = paramsList.at(offset);
|
|
params = param.trimmed() + TOOLTIP_PAGER_TPL.arg(offset+1).arg(paramsList.size());
|
|
}
|
|
}
|
|
tooltipSavedText = fName + " " + params;
|
|
tooltipSavedList.clear();
|
|
for (int i=0; i<paramsList.size(); i++) {
|
|
QString param = paramsList.at(i);
|
|
tooltipSavedList.append(fName + " " + param.replace("<", "<").replace(">", ">").trimmed() + TOOLTIP_PAGER_TPL.arg(i+1).arg(paramsList.size()));
|
|
}
|
|
tooltipSavedOrigName = fName;
|
|
tooltipSavedBlockNumber = cursor.block().blockNumber();
|
|
showTooltip(& cursor, tooltipSavedText);
|
|
followTooltip();
|
|
} else if (tooltipSavedText.size() > 0 && tooltipSavedBlockNumber == cursor.block().blockNumber()) {
|
|
QString fName = "", params = "";
|
|
int kSep = tooltipSavedText.indexOf("(");
|
|
if (kSep > 0) {
|
|
fName = tooltipSavedText.mid(0, kSep).trimmed();
|
|
params = tooltipSavedText.mid(kSep).trimmed();
|
|
}
|
|
if (fName == tooltipName) {
|
|
QString tooltipText = tooltipSavedOrigName + " " + params;
|
|
showTooltip(& cursor, tooltipText);
|
|
followTooltip();
|
|
}
|
|
} else if ((prevChar == ">" && prevPrevChar == "-") || (prevChar == ":" && prevPrevChar == ":")) {
|
|
cursor.setPosition(cursorP);
|
|
QString nsName = highlight->findNsPHPAtCursor(& block, cursor.positionInBlock());
|
|
QString clsName = highlight->findClsPHPAtCursor(& block, cursor.positionInBlock());
|
|
QString funcName = highlight->findFuncPHPAtCursor(& block, cursor.positionInBlock());
|
|
QString prevType = detectCompleteTypeAtCursorPHP(cursor, nsName, clsName, funcName);
|
|
if (prevType.size() > 0 && prevType.at(0) == "\\") prevType = prevType.mid(1);
|
|
if (prevType.size() > 0) {
|
|
CW->tooltipsIteratorPHP = CW->tooltipsPHP.find(prevType.toStdString()+"::"+tooltipName.toStdString());
|
|
if (CW->tooltipsIteratorPHP != CW->tooltipsPHP.end()) {
|
|
QString fName = QString::fromStdString(CW->tooltipsIteratorPHP->first);
|
|
QString params = QString::fromStdString(CW->tooltipsIteratorPHP->second);
|
|
fName = getFixedCompleteClassMethodName(fName, params);
|
|
params.replace("<", "<").replace(">", ">");
|
|
QStringList paramsList;
|
|
if (params.size() > 0 && params[0] == "(") {
|
|
paramsList = params.split(TOOLTIP_DELIMITER);
|
|
if (offset < 0 || offset >= paramsList.size()) offset = 0;
|
|
if (paramsList.size() > 1) {
|
|
QString param = paramsList.at(offset);
|
|
params = param.trimmed() + TOOLTIP_PAGER_TPL.arg(offset+1).arg(paramsList.size());
|
|
}
|
|
}
|
|
showTooltip(& cursor, fName + " " + params);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::followTooltip()
|
|
{
|
|
if (tooltipLabel.isVisible() && tooltipSavedText.size() > 0) {
|
|
QRegularExpressionMatch tMatch = functionParamsExpr.match(tooltipSavedText);
|
|
if (tMatch.capturedStart() >= 0 && tMatch.captured(2).trimmed().size() > 0) {
|
|
bool isVoid = tMatch.captured(2).trimmed() == "void";
|
|
QString tooltipName = tMatch.captured(1);
|
|
QStringList tooltipTextList = tMatch.captured(2).split(",");
|
|
QString tooltipEnd = tMatch.captured(3);
|
|
QTextCursor cursor = textCursor();
|
|
int blockNumberStart = cursor.block().blockNumber();
|
|
int cursPosStart = cursor.positionInBlock();
|
|
int cursPos = cursor.positionInBlock();
|
|
QRegularExpression tnExp("\\b"+QRegularExpression::escape(tooltipName.trimmed())+"\\b[\\s]*[(]", QRegularExpression::CaseInsensitiveOption);
|
|
int tnOffset = 0, thCaptured = -1, tooltipStart = -1;
|
|
do {
|
|
do {
|
|
QRegularExpressionMatch tnMatch = tnExp.match(cursor.block().text(), tnOffset);
|
|
thCaptured = tnMatch.capturedStart();
|
|
int tnLength = tnMatch.capturedLength();
|
|
if (thCaptured >= 0) {
|
|
if (cursPos >= 0 && thCaptured + tnLength > cursPos) break;
|
|
tooltipStart = thCaptured;
|
|
tnOffset = thCaptured + tnLength;
|
|
}
|
|
} while (thCaptured >= 0);
|
|
cursPos = -1;
|
|
tnOffset = 0;
|
|
} while(tooltipStart < 0 && cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor));
|
|
int tooltipOffset = 0;
|
|
if (tooltipStart >= 0) {
|
|
bool strSQOpen = false, strDQOpen = false;
|
|
int pCo = 0;
|
|
int blockNumber = cursor.block().blockNumber();
|
|
QString blockText = cursor.block().text();
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, tooltipStart);
|
|
while(cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor)) {
|
|
if (blockNumber != cursor.block().blockNumber()) {
|
|
blockNumber = cursor.block().blockNumber();
|
|
blockText = cursor.block().text();
|
|
}
|
|
QChar c = '\0';
|
|
int pos = cursor.positionInBlock();
|
|
if (blockNumber == blockNumberStart && pos > cursPosStart) break;
|
|
if (pos > 0) c = blockText[pos-1];
|
|
if (c == "'" && !strDQOpen) strSQOpen = !strSQOpen;
|
|
if (c == "\"" && !strSQOpen) strDQOpen = !strDQOpen;
|
|
if (c == ")" && !strSQOpen && !strDQOpen) pCo--;
|
|
if (c == "(" && !strSQOpen && !strDQOpen) pCo++;
|
|
if (c == "," && pCo == 1 && !strSQOpen && !strDQOpen) tooltipOffset++;
|
|
if ((c == ")" && pCo <= 0 && !strSQOpen && !strDQOpen) || c == ";" || c == "{" || c == "}") {
|
|
tooltipOffset = -1;
|
|
break;
|
|
}
|
|
if (blockNumber == blockNumberStart && pos >= blockText.size()) break;
|
|
}
|
|
if (tooltipOffset >= 0 && !isVoid) {
|
|
QString newText = tooltipSavedOrigName + " (";
|
|
for (int i=0; i<tooltipTextList.size(); i++) {
|
|
if (i > 0) newText += ",";
|
|
QString part = tooltipTextList.at(i);
|
|
if (i == tooltipOffset) {
|
|
part = tooltipBoldTagStart + part + tooltipBoldTagEnd;
|
|
}
|
|
newText += part;
|
|
}
|
|
newText += ")" + tooltipEnd;
|
|
tooltipLabel.setText(newText);
|
|
} else if (tooltipOffset < 0) {
|
|
hideTooltip();
|
|
}
|
|
} else {
|
|
hideTooltip();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::cursorPositionChanged()
|
|
{
|
|
if (!is_ready) return;
|
|
// highlight current line
|
|
QList<QTextEdit::ExtraSelection> extraSelections;
|
|
if (!isReadOnly()) {
|
|
highlightCurrentLine(& extraSelections);
|
|
highlightSearchWords(& extraSelections);
|
|
}
|
|
setExtraSelections(extraSelections);
|
|
lineAnnotation->hide();
|
|
if (!cursorPositionChangeLocked) {
|
|
cursorPositionChangeLocked = true;
|
|
QTimer::singleShot(INTERVAL_CURSOR_POS_CHANGED_MILLISECONDS, this, SLOT(cursorPositionChangedDelayed()));
|
|
}
|
|
// save cursor position
|
|
QTextCursor curs = textCursor();
|
|
int cursorPositionBlockNumber = curs.blockNumber();
|
|
if (cursorPositionBlockNumber != lastCursorPositionBlockNumber) {
|
|
lastCursorPositionBlockNumber = cursorPositionBlockNumber;
|
|
backPositions.append(curs.position());
|
|
if (backPositions.size() > 10) backPositions.removeFirst();
|
|
forwardPositions.clear();
|
|
emit backForwardChanged(getTabIndex());
|
|
}
|
|
#if defined(Q_OS_ANDROID)
|
|
if (mousePressTimer.isActive()) {
|
|
mousePressTimer.stop();
|
|
mousePressTimer.start();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Editor::cursorPositionChangedDelayed()
|
|
{
|
|
cursorPositionChangeLocked = false;
|
|
clearTextHoverFormat();
|
|
// highlight word
|
|
QTextCursor curs = textCursor();
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
int pos = curs.positionInBlock();
|
|
QChar prevChar = '\0', nextChar = '\0';
|
|
if (pos > 0 && curs.selectedText().size()==0) prevChar = blockText[pos - 1];
|
|
if (pos < total && curs.selectedText().size()==0) nextChar = blockText[pos];
|
|
// text under cursor
|
|
QString cursorText = "", prevText = "", nextText = "";
|
|
bool hasAlpha = false;
|
|
std::string mode = highlight->findModeAtCursor(&block, pos);
|
|
QChar cursorTextPrevChar = '\0';
|
|
int cursorTextPos = pos;
|
|
if (curs.selectedText().size()==0) {
|
|
for (int i=pos; i>0; i--) {
|
|
QString c = blockText.mid(i-1, 1);
|
|
if (!hasAlpha && isalpha(c[0].toLatin1())) hasAlpha = true;
|
|
cursorTextPrevChar = c[0];
|
|
if (mode == MODE_PHP || mode == MODE_JS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else {
|
|
if (isalnum(c[0].toLatin1()) || c=="_") prevText = c + prevText;
|
|
else break;
|
|
}
|
|
cursorTextPos = i-1;
|
|
}
|
|
for (int i=pos; i<total; i++) {
|
|
QString c = blockText.mid(i, 1);
|
|
if (!hasAlpha && isalpha(c[0].toLatin1())) hasAlpha = true;
|
|
if (mode == MODE_PHP || mode == MODE_JS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") nextText += c;
|
|
else break;
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") nextText += c;
|
|
else break;
|
|
} else {
|
|
if (isalnum(c[0].toLatin1()) || c=="_") nextText += c;
|
|
else break;
|
|
}
|
|
}
|
|
if (mode == MODE_PHP && prevText.size()==0 && nextText.size()>0 && hasAlpha &&
|
|
(nextText == "if" || nextText == "else" || nextText == "endif" || nextText == "switch" || nextText == "case" || nextText == "endswitch" || nextText == "while" || nextText == "endwhile" || nextText == "for" || nextText == "endfor" || nextText == "foreach" || nextText == "endforeach")
|
|
) {
|
|
cursorText = prevText + nextText;
|
|
} else if (mode == MODE_PHP && prevText.size()>0 && nextText.size()==0 && hasAlpha &&
|
|
(prevText == "endif" || prevText == "endswitch" || prevText == "endwhile" || prevText == "endfor" || prevText == "endforeach")
|
|
) {
|
|
cursorText = prevText + nextText;
|
|
} else if ((cursorTextPrevChar == "<" || cursorTextPrevChar == "/") && (prevText.size()>0 || nextText.size()>0) && hasAlpha) {
|
|
cursorText = prevText + nextText;
|
|
} else if (prevText.size()>0 && nextText.size()>0 && hasAlpha) {
|
|
cursorText = prevText + nextText;
|
|
}
|
|
} else {
|
|
cursorText = curs.selectedText();
|
|
}
|
|
highlightExtras(prevChar, nextChar, cursorTextPrevChar, cursorText, cursorTextPos, mode);
|
|
if (showBreadcrumbs) {
|
|
QString scopeName = "";
|
|
if (mode == MODE_PHP) {
|
|
followTooltip();
|
|
//QString nsName = highlight->findNsPHPAtCursor(& block, pos);
|
|
QString clsName = highlight->findClsPHPAtCursor(& block, pos);
|
|
QString funcName = highlight->findFuncPHPAtCursor(& block, pos);
|
|
if (funcName == "anonymous function") clsName = "";
|
|
else if (funcName.size() > 0 && clsName == "anonymous class") funcName = "";
|
|
//if (nsName.size() > 0) scopeName += nsName + BREADCRUMBS_DELIMITER;
|
|
if (clsName.size() > 0) scopeName += clsName + BREADCRUMBS_DELIMITER;
|
|
if (funcName.size() > 0) scopeName += funcName;
|
|
} else if (mode == MODE_JS) {
|
|
followTooltip();
|
|
QString clsName = highlight->findClsJSAtCursor(& block, pos);
|
|
QString funcName = highlight->findFuncJSAtCursor(& block, pos);
|
|
if (clsName == "anonymous class" || funcName == "anonymous function") clsName = "";
|
|
if (clsName.size() > 0) scopeName += clsName + BREADCRUMBS_DELIMITER;
|
|
if (funcName.size() > 0) scopeName += funcName;
|
|
} else if (mode == MODE_CSS) {
|
|
QString mediaName = highlight->findMediaCSSAtCursor(& block, pos);
|
|
if (mediaName.size() > 0) {
|
|
scopeName += "@media ( " + mediaName.trimmed() + " )";
|
|
}
|
|
} else if (mode == MODE_HTML) {
|
|
QString tagChain = highlight->findTagChainHTMLAtCursor(& block, pos);
|
|
if (tagChain.size() > 0) {
|
|
scopeName += tagChain.replace(",", BREADCRUMBS_DELIMITER);
|
|
}
|
|
} else {
|
|
scopeName = "";
|
|
}
|
|
setBreadcrumbsText(scopeName.trimmed());
|
|
}
|
|
showLineAnnotation();
|
|
if (isReady()) emit statusBarText(tabIndex, ""); // update status bar
|
|
}
|
|
|
|
int Editor::findFirstVisibleBlockIndex()
|
|
{
|
|
if (verticalScrollBar()->sliderPosition() <= FIRST_BLOCK_BIN_SEARCH_SCROLL_VALUE) {
|
|
return searchFirstVisibleBlockIndexLinear();
|
|
} else {
|
|
return searchFirstVisibleBlockIndexBinary();
|
|
}
|
|
}
|
|
|
|
int Editor::searchFirstVisibleBlockIndexLinear()
|
|
{
|
|
int top = contentsMargins().top();
|
|
QTextCursor curs = QTextCursor(document());
|
|
curs.movePosition(QTextCursor::Start);
|
|
for(int i = 0; i < document()->blockCount(); i++)
|
|
{
|
|
QTextBlock block = curs.block();
|
|
int block_y = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).y());
|
|
if (verticalScrollBar()->sliderPosition()<=block_y) {
|
|
return i;
|
|
}
|
|
curs.movePosition(QTextCursor::NextBlock);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Editor::searchFirstVisibleBlockIndexBinary()
|
|
{
|
|
int top = contentsMargins().top();
|
|
int total = document()->blockCount();
|
|
if (total < 2) return 0;
|
|
int offsetLeft = 0, offsetRight = total;
|
|
int blockNumber = 0;
|
|
do {
|
|
blockNumber = offsetLeft + (offsetRight - offsetLeft) / 2;
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
int block_y = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).y());
|
|
if (verticalScrollBar()->sliderPosition()>block_y) {
|
|
offsetLeft = blockNumber;
|
|
blockNumber++; // block is partly visible ?
|
|
if (offsetLeft + 1 >= offsetRight) break;
|
|
} else if (verticalScrollBar()->sliderPosition()<block_y) {
|
|
offsetRight = blockNumber;
|
|
} else {
|
|
break;
|
|
}
|
|
} while (offsetLeft < offsetRight);
|
|
return blockNumber;
|
|
}
|
|
|
|
int Editor::findLastVisibleBlockIndex()
|
|
{
|
|
int top = contentsMargins().top();
|
|
int bottom = contentsMargins().bottom();
|
|
int first = getFirstVisibleBlockIndex();
|
|
QTextCursor curs = QTextCursor(document());
|
|
curs.movePosition(QTextCursor::Start);
|
|
if (first > 0) curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, first);
|
|
for(int i = first; i < document()->blockCount(); i++)
|
|
{
|
|
QTextBlock block = curs.block();
|
|
int block_y = static_cast<int>(document()->documentLayout()->blockBoundingRect(block).y());
|
|
int block_h = static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
if (viewport()->geometry().height()+verticalScrollBar()->sliderPosition()-top-bottom<block_y+block_h) {
|
|
if (i > 0) i--; // block is partly visible ?
|
|
return i;
|
|
} else if (viewport()->geometry().height()+verticalScrollBar()->sliderPosition()-top-bottom==block_y+block_h) {
|
|
return i;
|
|
}
|
|
curs.movePosition(QTextCursor::NextBlock);
|
|
}
|
|
return document()->blockCount()-1;
|
|
}
|
|
|
|
int Editor::getFirstVisibleBlockIndex()
|
|
{
|
|
if (firstVisibleBlockIndex < 0) {
|
|
firstVisibleBlockIndex = findFirstVisibleBlockIndex();
|
|
}
|
|
return firstVisibleBlockIndex;
|
|
}
|
|
|
|
int Editor::getLastVisibleBlockIndex()
|
|
{
|
|
if (lastVisibleBlockIndex < 0) {
|
|
lastVisibleBlockIndex = findLastVisibleBlockIndex();
|
|
}
|
|
return lastVisibleBlockIndex;
|
|
}
|
|
|
|
void Editor::setBreadcrumbsText(QString text) {
|
|
static_cast<Breadcrumbs *>(breadcrumbs)->setText(text);
|
|
breadcrumbs->update();
|
|
}
|
|
|
|
void Editor::breadcrumbsPaintEvent(QPaintEvent *event)
|
|
{
|
|
int errorsCount = static_cast<LineMark *>(lineMark)->getErrorsCount();
|
|
int warningsCount = static_cast<LineMark *>(lineMark)->getWarningsCount();
|
|
|
|
QPainter painter(breadcrumbs);
|
|
if (errorsCount > 0) {
|
|
painter.fillRect(event->rect(), breadcrumbsErrorBgColor);
|
|
} else if (warningsCount > 0) {
|
|
painter.fillRect(event->rect(), breadcrumbsWarningBgColor);
|
|
} else {
|
|
painter.fillRect(event->rect(), breadcrumbsBgColor);
|
|
}
|
|
|
|
painter.setPen(widgetBorderColor);
|
|
painter.drawLine(0, breadcrumbs->height()-1, breadcrumbs->width(), breadcrumbs->height()-1);
|
|
|
|
QFontMetrics fm(editorBreadcrumbsFont);
|
|
int lineW = lineNumberAreaWidth();
|
|
int markW = lineMarkAreaWidth();
|
|
int offset = (breadcrumbs->height()-fm.height())/2;
|
|
if (offset < 0) offset = 0;
|
|
|
|
bool pretty = true;
|
|
painter.setPen(breadcrumbsColor);
|
|
QString text = static_cast<Breadcrumbs *>(breadcrumbs)->getText();
|
|
if (pretty) {
|
|
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
|
QIcon delimiter = Icon::get("separator", QIcon(":/icons/separator.png"));
|
|
int listOffset = 0;
|
|
/*
|
|
int listMargin = 10;
|
|
int lineSize = 2;
|
|
*/
|
|
QStringList textList = text.split(BREADCRUMBS_DELIMITER.trimmed());
|
|
for (int i=0; i<textList.size(); i++) {
|
|
QString _text = textList.at(i);
|
|
_text = _text.trimmed();
|
|
if (_text.size() == 0) continue;
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
int width = fm.width(_text);
|
|
*/
|
|
int width = fm.horizontalAdvance(_text);
|
|
painter.drawText(lineW+markW+listOffset, offset, width, fm.height(), Qt::AlignLeft, _text);
|
|
/*
|
|
listOffset += width+listMargin-listMargin/4;
|
|
QPointF p1(lineW+markW+listOffset, breadcrumbs->height()/4);
|
|
QPointF p2(lineW+markW+listOffset+listMargin/2, breadcrumbs->height()/2);
|
|
QPointF p3(lineW+markW+listOffset, breadcrumbs->height()-breadcrumbs->height()/4-1);
|
|
painter.drawLine(p1, p2);
|
|
painter.drawLine(p2, p3);
|
|
listOffset += listMargin+listMargin/4+lineSize;
|
|
*/
|
|
listOffset += width;
|
|
painter.drawPixmap(lineW+markW+listOffset, 0, breadcrumbs->height(), breadcrumbs->height(), delimiter.pixmap(64, 64));
|
|
listOffset += breadcrumbs->height();
|
|
}
|
|
} else {
|
|
painter.drawText(lineW+markW, offset, breadcrumbs->width(), fm.height(), Qt::AlignLeft, text);
|
|
}
|
|
}
|
|
|
|
void Editor::searchPaintEvent(QPaintEvent *event)
|
|
{
|
|
QPainter painter(search);
|
|
painter.fillRect(event->rect(), searchBgColor);
|
|
|
|
painter.setPen(widgetBorderColor);
|
|
painter.drawLine(0, 0, search->width(), 0);
|
|
}
|
|
|
|
void Editor::lineMarkAreaPaintEvent(QPaintEvent *event)
|
|
{
|
|
QPainter painter(lineMark);
|
|
painter.fillRect(event->rect(), lineMarkBgColor);
|
|
int markW = lineMarkAreaWidth();
|
|
QFontMetrics fm(editorFont);
|
|
|
|
int blockNumber = getFirstVisibleBlockIndex();
|
|
if (blockNumber < 0) return;
|
|
if (blockNumber>0) blockNumber--;
|
|
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
//int top = contentsMargins().top();
|
|
int top = 0; // widget has offset
|
|
|
|
if (blockNumber == 0) {
|
|
top += static_cast<int>(document()->documentMargin()) - verticalScrollBar()->sliderPosition();
|
|
} else {
|
|
QTextBlock prev_block = document()->findBlockByNumber(blockNumber-1);
|
|
int prev_y = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).y());
|
|
int prev_h = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).height());
|
|
top += prev_y + prev_h - verticalScrollBar()->sliderPosition();
|
|
}
|
|
|
|
int bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
|
|
while (block.isValid() && top <= event->rect().bottom()) {
|
|
if (block.isVisible()) {
|
|
int line = blockNumber + 1;
|
|
if (gitDiffLines.size() > 0 && gitDiffLines.contains(line)) {
|
|
Git::DiffLine mLine = gitDiffLines.value(line);
|
|
if (mLine.isModified) {
|
|
painter.fillRect(0, top, markW, bottom - top, lineNumberModifiedBgColor);
|
|
}
|
|
if (mLine.isDeleted) {
|
|
painter.fillRect(0, top, markW, 1, lineNumberDeletedBorderColor);
|
|
}
|
|
}
|
|
QString errorText = "", warningText = "", markText = "";
|
|
int error = static_cast<LineMark *>(lineMark)->getError(line, errorText);
|
|
int warning = static_cast<LineMark *>(lineMark)->getWarning(line, warningText);
|
|
int mark = static_cast<LineMark *>(lineMark)->getMark(line, markText);
|
|
if (error > 0 || warning > 0 || mark > 0) {
|
|
if (error > 0) {
|
|
painter.fillRect(markW-LINE_MARK_WIDGET_RECT_WIDTH, top, LINE_MARK_WIDGET_RECT_WIDTH, fm.height(), lineErrorRectColor);
|
|
} else if (warning > 0) {
|
|
painter.fillRect(markW-LINE_MARK_WIDGET_RECT_WIDTH, top, LINE_MARK_WIDGET_RECT_WIDTH, fm.height(), lineWarningRectColor);
|
|
}
|
|
if (mark > 0) {
|
|
painter.fillRect(markW-LINE_MARK_WIDGET_LINE_WIDTH, top, LINE_MARK_WIDGET_LINE_WIDTH, bottom-top, lineMarkRectColor);
|
|
}
|
|
}
|
|
int markRectWidth = LINE_MARK_WIDGET_RECT_WIDTH;
|
|
if (error >= 0 || warning >= 0) markRectWidth /= 2;
|
|
markPointsIterator = markPoints.find(line);
|
|
if (markPointsIterator != markPoints.end()) {
|
|
painter.fillRect(markW-markRectWidth, top, markRectWidth, fm.height(), lineMarkRectColor);
|
|
}
|
|
}
|
|
block = block.next();
|
|
top = bottom;
|
|
bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
blockNumber++;
|
|
}
|
|
}
|
|
|
|
void Editor::lineMapAreaPaintEvent(QPaintEvent *event)
|
|
{
|
|
QPainter painter(lineMap);
|
|
painter.fillRect(event->rect(), lineMapBgColor);
|
|
QVector<int> marks = static_cast<LineMap *>(lineMap)->getMarks();
|
|
QVector<int> warnings = static_cast<LineMap *>(lineMap)->getWarnings();
|
|
QVector<int> errors = static_cast<LineMap *>(lineMap)->getErrors();
|
|
int mapW = lineMapAreaWidth();
|
|
int height = lineMap->geometry().height();
|
|
if (verticalScrollBar()->isVisible() && height > verticalScrollBar()->geometry().height()) {
|
|
height = verticalScrollBar()->geometry().height();
|
|
}
|
|
if (verticalScrollBar()->isVisible()){
|
|
int sHeight = verticalScrollBar()->value() * height / verticalScrollBar()->maximum();
|
|
painter.fillRect(0, 0, mapW, height, lineMapScrollAreaBgColor);
|
|
painter.fillRect(0, 0, mapW, sHeight, lineMapScrollBgColor);
|
|
}
|
|
int lines = qMax(1, document()->blockCount());
|
|
int offset = (height / lines) / 2;
|
|
painter.setPen(lineMarkColor);
|
|
for (int i=0; i<marks.size(); i++){
|
|
int top = marks.at(i) * height / lines;
|
|
if (top > height) continue;
|
|
top -= offset;
|
|
if (top < 1) top = 1;
|
|
if (top > height-1) top = height-1;
|
|
painter.drawLine(QPoint(0, top), QPoint(mapW, top));
|
|
}
|
|
for (auto & iterator : markPoints) {
|
|
int top = iterator.first * height / lines;
|
|
if (top > height) continue;
|
|
top -= offset;
|
|
if (top < 1) top = 1;
|
|
if (top > height-1) top = height-1;
|
|
painter.drawLine(QPoint(0, top), QPoint(mapW, top));
|
|
}
|
|
for (auto & iterator: modifiedLines) {
|
|
int top = iterator.first * height / lines;
|
|
if (top > height) continue;
|
|
top -= offset;
|
|
if (top < 1) top = 1;
|
|
if (top > height-1) top = height-1;
|
|
painter.drawLine(QPoint(mapW / 2, top), QPoint(mapW, top));
|
|
}
|
|
painter.setPen(lineWarningColor);
|
|
for (int i=0; i<warnings.size(); i++){
|
|
int top = warnings.at(i) * height / lines;
|
|
if (top > height) continue;
|
|
top -= offset;
|
|
if (top < 1) top = 1;
|
|
if (top > height-1) top = height-1;
|
|
painter.drawLine(QPoint(0, top), QPoint(mapW, top));
|
|
}
|
|
painter.setPen(lineErrorColor);
|
|
for (int i=0; i<errors.size(); i++){
|
|
int top = errors.at(i) * height / lines;
|
|
if (top > height) continue;
|
|
top -= offset;
|
|
if (top < 1) top = 1;
|
|
if (top > height-1) top = height-1;
|
|
painter.drawLine(QPoint(0, top), QPoint(mapW, top));
|
|
}
|
|
// draw highlight progress
|
|
if (highlightProgressPercent > 0 && highlightProgressPercent < 100){
|
|
int pHeight = highlightProgressPercent * height / 100;
|
|
painter.fillRect(0, 0, LINE_MAP_PROGRESS_WIDTH, pHeight, progressColor);
|
|
}
|
|
// draw spell progress
|
|
if (spellProgressPercent > 0 && spellProgressPercent < 100){
|
|
int p = spellProgressPercent * height / 100;
|
|
int pHeight = LINE_MAP_PROGRESS_HEIGHT;
|
|
int y = p - (pHeight / 2);
|
|
if (y < 0) y = 0;
|
|
if (y + pHeight > height) pHeight = height - y;
|
|
painter.fillRect(0, y, LINE_MAP_PROGRESS_WIDTH, pHeight, progressColor);
|
|
}
|
|
}
|
|
|
|
void Editor::lineNumberAreaPaintEvent(QPaintEvent *event)
|
|
{
|
|
QPainter painter(lineNumber);
|
|
painter.fillRect(event->rect(), lineNumberBgColor);
|
|
|
|
int blockNumber = getFirstVisibleBlockIndex();
|
|
if (blockNumber < 0) return;
|
|
if (blockNumber>0) blockNumber--;
|
|
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
QFontMetrics fm(editorFont);
|
|
//int top = contentsMargins().top();
|
|
int top = 0; // widget has offset
|
|
|
|
if (blockNumber == 0) {
|
|
top += static_cast<int>(document()->documentMargin()) - verticalScrollBar()->sliderPosition();
|
|
} else {
|
|
QTextBlock prev_block = document()->findBlockByNumber(blockNumber-1);
|
|
int prev_y = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).y());
|
|
int prev_h = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).height());
|
|
top += prev_y + prev_h - verticalScrollBar()->sliderPosition();
|
|
}
|
|
|
|
int bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
|
|
while (block.isValid() && top <= event->rect().bottom()) {
|
|
if (block.isVisible()) {
|
|
QString number = QString::number(blockNumber + 1);
|
|
if (gitDiffLines.size() > 0 && gitDiffLines.contains(blockNumber + 1)) {
|
|
Git::DiffLine mLine = gitDiffLines.value(blockNumber + 1);
|
|
if (mLine.isModified) {
|
|
painter.fillRect(0, top, lineNumber->width(), bottom - top, lineNumberModifiedBgColor);
|
|
painter.setPen(lineNumberModifiedColor);
|
|
} else {
|
|
painter.setPen(lineNumberColor);
|
|
}
|
|
if (mLine.isDeleted) {
|
|
painter.fillRect(0, top, lineNumber->width(), 1, lineNumberDeletedBorderColor);
|
|
}
|
|
} else {
|
|
modifiedLinesIterator = modifiedLines.find(blockNumber + 1);
|
|
if (modifiedLinesIterator != modifiedLines.end()) {
|
|
painter.fillRect(0, top, LINE_NUMBER_WIDGET_PADDING / 2, bottom - top, lineNumberModifiedBgColor);
|
|
}
|
|
painter.setPen(lineNumberColor);
|
|
}
|
|
painter.drawText(0, top, lineNumber->width(), fm.height(), Qt::AlignRight, number);
|
|
}
|
|
block = block.next();
|
|
top = bottom;
|
|
bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
blockNumber++;
|
|
}
|
|
}
|
|
|
|
void Editor::highlightCloseCharPair(QChar openChar, QChar closeChar, QList<QTextEdit::ExtraSelection> * extraSelections)
|
|
{
|
|
QTextCursor cursor = textCursor();
|
|
int pos = cursor.positionInBlock()-1;
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData != nullptr && blockData->specialChars.size()>0 && blockData->specialCharsPos.size()>0 && blockData->specialChars.size()==blockData->specialCharsPos.size()) {
|
|
bool sFound = false;
|
|
int count = 0;
|
|
int positionInBlock = -1;
|
|
int iterations = 0;
|
|
do {
|
|
if (blockData->specialChars.size()>0 && blockData->specialCharsPos.size()>0) {
|
|
for (int i=blockData->specialChars.size()-1; i>=0; i--) {
|
|
iterations++;
|
|
if (iterations > SEARCH_LIMIT) break;
|
|
QChar c = blockData->specialChars.at(i);
|
|
if (!sFound && c == closeChar && blockData->specialCharsPos.at(i) == pos) {
|
|
sFound = true;
|
|
} else if (sFound && c == closeChar) {
|
|
count++;
|
|
} else if (sFound && c == openChar && count > 0) {
|
|
count--;
|
|
} else if (sFound && c == openChar && count == 0) {
|
|
positionInBlock = blockData->specialCharsPos.at(i);
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
int absPos = cursor.position();
|
|
cursor.setPosition(absPos+positionInBlock, QTextCursor::MoveAnchor);
|
|
//cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, positionInBlock);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
|
QTextEdit::ExtraSelection charSelection;
|
|
charSelection.format.setBackground(selectedCharBgColor);
|
|
charSelection.format.setForeground(selectedCharColor);
|
|
charSelection.cursor = cursor;
|
|
extraSelections->append(charSelection);
|
|
|
|
QTextCursor cursorPair = textCursor();
|
|
cursorPair.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
|
QTextEdit::ExtraSelection charSelectionPair;
|
|
charSelectionPair.format.setBackground(selectedCharBgColor);
|
|
charSelectionPair.format.setForeground(selectedCharColor);
|
|
charSelectionPair.cursor = cursorPair;
|
|
extraSelections->append(charSelectionPair);
|
|
|
|
// line mark
|
|
if (cursor.block().blockNumber() < cursorPair.block().blockNumber()) {
|
|
for (int i=cursor.block().blockNumber(); i<=cursorPair.block().blockNumber(); i++) {
|
|
static_cast<LineMark *>(lineMark)->addMark(i+1);
|
|
}
|
|
}
|
|
// tooltip
|
|
int firstBlockIndex = getFirstVisibleBlockIndex();
|
|
if (cursor.block().blockNumber() != cursorPair.block().blockNumber() && cursor.block().blockNumber() < firstBlockIndex) {
|
|
QString tooltipText = cursor.block().text();
|
|
while(tooltipText.trimmed() == "{" && cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) {
|
|
QString prevTooltipText = cursor.block().text();
|
|
if (prevTooltipText.trimmed().size() > 0) {
|
|
tooltipText = prevTooltipText + " " + tooltipText.trimmed();
|
|
positionInBlock = prevTooltipText.size() + 1;
|
|
}
|
|
}
|
|
int xOffset = 0;
|
|
if (positionInBlock >= 0) {
|
|
QRegularExpression indentExpr = QRegularExpression("^[\\s]+");
|
|
QRegularExpressionMatch indentMatch = indentExpr.match(tooltipText);
|
|
if (indentMatch.capturedStart()==0) {
|
|
QString indent = indentMatch.captured();
|
|
int spaces = indent.count(" ");
|
|
int tabs = indent.count("\t");
|
|
int cursOffset = spaces + tabs * tabWidth;
|
|
QFontMetrics fm(editorFont);
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
xOffset = fm.width(" ") * cursOffset - horizontalScrollBar()->sliderPosition();
|
|
*/
|
|
xOffset = fm.horizontalAdvance(" ") * cursOffset - horizontalScrollBar()->sliderPosition();
|
|
if (xOffset < 0) xOffset = 0;
|
|
}
|
|
}
|
|
tooltipSavedText = "";
|
|
tooltipSavedList.clear();
|
|
tooltipSavedPageOffset = -1;
|
|
tooltipSavedOrigName = "";
|
|
tooltipSavedBlockNumber = -1;
|
|
showTooltip(xOffset+lineNumberAreaWidth()+lineMarkAreaWidth(), 0, tooltipText.trimmed(), false);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!sFound) break;
|
|
if (!cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) break;
|
|
blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData == nullptr || blockData->specialChars.size()!=blockData->specialCharsPos.size()) break;
|
|
} while(positionInBlock < 0);
|
|
}
|
|
}
|
|
|
|
void Editor::highlightOpenCharPair(QChar openChar, QChar closeChar, QList<QTextEdit::ExtraSelection> * extraSelections)
|
|
{
|
|
QTextCursor cursor = textCursor();
|
|
int pos = cursor.positionInBlock();
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData != nullptr && blockData->specialChars.size()>0 && blockData->specialCharsPos.size()>0 && blockData->specialChars.size()==blockData->specialCharsPos.size()) {
|
|
bool sFound = false;
|
|
int count = 0;
|
|
int positionInBlock = -1;
|
|
int iterations = 0;
|
|
do {
|
|
if (blockData->specialChars.size()>0 && blockData->specialCharsPos.size()>0) {
|
|
for (int i=0; i<blockData->specialChars.size(); i++) {
|
|
iterations++;
|
|
if (iterations > SEARCH_LIMIT) break;
|
|
QChar c = blockData->specialChars.at(i);
|
|
if (!sFound && c == openChar && blockData->specialCharsPos.at(i) == pos) {
|
|
sFound = true;
|
|
} else if (sFound && c == openChar) {
|
|
count++;
|
|
} else if (sFound && c == closeChar && count > 0) {
|
|
count--;
|
|
} else if (sFound && c == closeChar && count == 0) {
|
|
positionInBlock = blockData->specialCharsPos.at(i);
|
|
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
int absPos = cursor.position();
|
|
cursor.setPosition(absPos+positionInBlock, QTextCursor::MoveAnchor);
|
|
//cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, positionInBlock);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
|
QTextEdit::ExtraSelection charSelection;
|
|
charSelection.format.setBackground(selectedCharBgColor);
|
|
charSelection.format.setForeground(selectedCharColor);
|
|
charSelection.cursor = cursor;
|
|
extraSelections->append(charSelection);
|
|
|
|
QTextCursor cursorPair = textCursor();
|
|
cursorPair.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
|
QTextEdit::ExtraSelection charSelectionPair;
|
|
charSelectionPair.format.setBackground(selectedCharBgColor);
|
|
charSelectionPair.format.setForeground(selectedCharColor);
|
|
charSelectionPair.cursor = cursorPair;
|
|
extraSelections->append(charSelectionPair);
|
|
|
|
// line mark
|
|
if (cursor.block().blockNumber() > cursorPair.block().blockNumber()) {
|
|
for (int i=cursorPair.block().blockNumber(); i<=cursor.block().blockNumber(); i++) {
|
|
static_cast<LineMark *>(lineMark)->addMark(i+1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!sFound) break;
|
|
if (!cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor)) break;
|
|
blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData == nullptr || blockData->specialChars.size()!=blockData->specialCharsPos.size()) break;
|
|
} while(positionInBlock < 0);
|
|
}
|
|
}
|
|
|
|
void Editor::highlightCloseTagPair(QString tagName, int pos, QList<QTextEdit::ExtraSelection> * extraSelections)
|
|
{
|
|
QTextCursor cursor = textCursor();
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData != nullptr && blockData->specialWords.size()>0 && blockData->specialWordsPos.size()>0 && blockData->specialWords.size()==blockData->specialWordsPos.size()) {
|
|
bool sFound = false;
|
|
int count = 0;
|
|
int positionInBlock = -1;
|
|
int iterations = 0;
|
|
do {
|
|
if (blockData->specialWords.size()>0 && blockData->specialWordsPos.size()>0) {
|
|
for (int i=blockData->specialWords.size()-1; i>=0; i--) {
|
|
iterations++;
|
|
if (iterations > SEARCH_LIMIT) break;
|
|
QString w = blockData->specialWords.at(i);
|
|
if (!sFound && w == "/"+tagName && blockData->specialWordsPos.at(i) == pos) {
|
|
sFound = true;
|
|
} else if (sFound && w == "/"+tagName) {
|
|
count++;
|
|
} else if (sFound && w == tagName && count > 0) {
|
|
count--;
|
|
} else if (sFound && w == tagName && count == 0) {
|
|
positionInBlock = blockData->specialWordsPos.at(i);
|
|
bool _found = false;
|
|
int _start = -1, _length = 0, _offset = 0;
|
|
QString blockText = cleanUpText(cursor.block().text());
|
|
do {
|
|
QRegularExpressionMatch _match = tagExpr.match(blockText, _offset);
|
|
_start = _match.capturedStart();
|
|
if (_start>=0) {
|
|
_length = _match.capturedLength();
|
|
if (_start < positionInBlock && _start+_length > positionInBlock) {
|
|
_found = true;
|
|
break;
|
|
}
|
|
_offset = _start + _length;
|
|
}
|
|
} while (_start>=0);
|
|
if (!_found) {
|
|
_start = positionInBlock;
|
|
_length = tagName.size();
|
|
}
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, _start);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, _length);
|
|
QTextEdit::ExtraSelection charSelection;
|
|
charSelection.format.setBackground(selectedTagBgColor);
|
|
charSelection.cursor = cursor;
|
|
extraSelections->append(charSelection);
|
|
|
|
QTextCursor cursorPair = textCursor();
|
|
_found = false;
|
|
_start = -1; _length = 0; _offset = 0;
|
|
QString blockTextPair = cleanUpText(cursorPair.block().text());
|
|
do {
|
|
QRegularExpressionMatch _match = tagExpr.match(blockTextPair, _offset);
|
|
_start = _match.capturedStart();
|
|
if (_start>=0) {
|
|
_length = _match.capturedLength();
|
|
if (_start < pos && _start+_length > pos) {
|
|
_found = true;
|
|
break;
|
|
}
|
|
_offset = _start + _length;
|
|
}
|
|
} while (_start>=0);
|
|
if (!_found) {
|
|
_start = pos;
|
|
_length = tagName.size();
|
|
}
|
|
cursorPair.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursorPair.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, _start);
|
|
cursorPair.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, _length);
|
|
QTextEdit::ExtraSelection charSelectionPair;
|
|
charSelectionPair.format.setBackground(selectedTagBgColor);
|
|
charSelectionPair.cursor = cursorPair;
|
|
extraSelections->append(charSelectionPair);
|
|
|
|
// line mark
|
|
if (cursor.block().blockNumber() < cursorPair.block().blockNumber()) {
|
|
for (int i=cursor.block().blockNumber(); i<=cursorPair.block().blockNumber(); i++) {
|
|
static_cast<LineMark *>(lineMark)->addMark(i+1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!sFound) break;
|
|
if (!cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) break;
|
|
blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData == nullptr || blockData->specialWords.size()!=blockData->specialWordsPos.size()) break;
|
|
} while(positionInBlock < 0);
|
|
}
|
|
}
|
|
|
|
void Editor::highlightOpenTagPair(QString tagName, int pos, QList<QTextEdit::ExtraSelection> * extraSelections)
|
|
{
|
|
QTextCursor cursor = textCursor();
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData != nullptr && blockData->specialWords.size()>0 && blockData->specialWordsPos.size()>0 && blockData->specialWords.size()==blockData->specialWordsPos.size()) {
|
|
bool sFound = false;
|
|
int count = 0;
|
|
int positionInBlock = -1;
|
|
int iterations = 0;
|
|
do {
|
|
if (blockData->specialWords.size()>0 && blockData->specialWordsPos.size()>0) {
|
|
for (int i=0; i<blockData->specialWords.size(); i++) {
|
|
iterations++;
|
|
if (iterations > SEARCH_LIMIT) break;
|
|
QString w = blockData->specialWords.at(i);
|
|
if (!sFound && w == tagName && blockData->specialWordsPos.at(i) == pos) {
|
|
sFound = true;
|
|
} else if (sFound && w == tagName) {
|
|
count++;
|
|
} else if (sFound && w == "/"+tagName && count > 0) {
|
|
count--;
|
|
} else if (sFound && w == "/"+tagName && count == 0) {
|
|
positionInBlock = blockData->specialWordsPos.at(i);
|
|
bool _found = false;
|
|
int _start = -1, _length = 0, _offset = 0;
|
|
QString blockText = cleanUpText(cursor.block().text());
|
|
do {
|
|
QRegularExpressionMatch _match = tagExpr.match(blockText, _offset);
|
|
_start = _match.capturedStart();
|
|
if (_start>=0) {
|
|
_length = _match.capturedLength();
|
|
if (_start < positionInBlock && _start+_length > positionInBlock) {
|
|
_found = true;
|
|
break;
|
|
}
|
|
_offset = _start + _length;
|
|
}
|
|
} while (_start>=0);
|
|
if (!_found) {
|
|
_start = positionInBlock;
|
|
_length = tagName.size();
|
|
}
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, _start);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, _length);
|
|
QTextEdit::ExtraSelection charSelection;
|
|
charSelection.format.setBackground(selectedTagBgColor);
|
|
charSelection.cursor = cursor;
|
|
extraSelections->append(charSelection);
|
|
|
|
QTextCursor cursorPair = textCursor();
|
|
_found = false;
|
|
_start = -1; _length = 0; _offset = 0;
|
|
QString blockTextPair = cleanUpText(cursorPair.block().text());
|
|
do {
|
|
QRegularExpressionMatch _match = tagExpr.match(blockTextPair, _offset);
|
|
_start = _match.capturedStart();
|
|
if (_start>=0) {
|
|
_length = _match.capturedLength();
|
|
if (_start < pos && _start+_length > pos) {
|
|
_found = true;
|
|
break;
|
|
}
|
|
_offset = _start + _length;
|
|
}
|
|
} while (_start>=0);
|
|
if (!_found) {
|
|
_start = pos;
|
|
_length = tagName.size();
|
|
}
|
|
cursorPair.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursorPair.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, _start);
|
|
cursorPair.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, _length);
|
|
QTextEdit::ExtraSelection charSelectionPair;
|
|
charSelectionPair.format.setBackground(selectedTagBgColor);
|
|
charSelectionPair.cursor = cursorPair;
|
|
extraSelections->append(charSelectionPair);
|
|
|
|
// line mark
|
|
if (cursor.block().blockNumber() > cursorPair.block().blockNumber()) {
|
|
for (int i=cursorPair.block().blockNumber(); i<=cursor.block().blockNumber(); i++) {
|
|
static_cast<LineMark *>(lineMark)->addMark(i+1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!sFound) break;
|
|
if (!cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor)) break;
|
|
blockData = dynamic_cast<HighlightData *>(cursor.block().userData());
|
|
if (blockData == nullptr || blockData->specialWords.size()!=blockData->specialWordsPos.size()) break;
|
|
} while(positionInBlock < 0);
|
|
}
|
|
}
|
|
|
|
void Editor::highlightPHPCloseSpecialTagPair(QString tagName, int pos, QList<QTextEdit::ExtraSelection> * extraSelections)
|
|
{
|
|
QRegularExpression openTagExpr, closeTagExpr, foundTagExpr;
|
|
if (tagName == "endif" || tagName == "else") {
|
|
openTagExpr = tagExprIf;
|
|
closeTagExpr = tagExprEndif;
|
|
if (tagName == "else") foundTagExpr = tagExprElse;
|
|
else foundTagExpr = closeTagExpr;
|
|
} else if (tagName == "endswitch" || tagName == "case") {
|
|
openTagExpr = tagExprSwitch;
|
|
closeTagExpr = tagExprEndswitch;
|
|
if (tagName == "case") foundTagExpr = tagExprCase;
|
|
else foundTagExpr = closeTagExpr;
|
|
} else if (tagName == "endwhile") {
|
|
openTagExpr = tagExprWhile;
|
|
closeTagExpr = tagExprEndwhile;
|
|
foundTagExpr = closeTagExpr;
|
|
} else if (tagName == "endfor") {
|
|
openTagExpr = tagExprFor;
|
|
closeTagExpr = tagExprEndfor;
|
|
foundTagExpr = closeTagExpr;
|
|
} else if (tagName == "endforeach") {
|
|
openTagExpr = tagExprForeach;
|
|
closeTagExpr = tagExprEndforeach;
|
|
foundTagExpr = closeTagExpr;
|
|
} else {
|
|
return;
|
|
}
|
|
QTextCursor cursor = textCursor();
|
|
QString blockText = cursor.block().text();
|
|
bool sFound = false;
|
|
int maxOffset = blockText.size();
|
|
int count = 0;
|
|
int positionInBlock = -1;
|
|
int lengthInBlock = 0;
|
|
int closePositionInBlock = -1;
|
|
int closeLengthInBlock = 0;
|
|
int iterations = 0;
|
|
do {
|
|
QVector<int> openPos, openLength, closePos, closeLength, sortedPos, sortedLength, sortedTag;
|
|
int offset = 0, start = -1;
|
|
do {
|
|
iterations++;
|
|
if (iterations > SEARCH_LIMIT) break;
|
|
QRegularExpressionMatch match;
|
|
if (!sFound) {
|
|
match = foundTagExpr.match(blockText, offset);
|
|
} else {
|
|
match = closeTagExpr.match(blockText, offset);
|
|
}
|
|
start = match.capturedStart();
|
|
if (start>=0) {
|
|
int length = match.capturedLength();
|
|
offset = start + length;
|
|
if (!sFound && start<=pos && start+length>=pos) {
|
|
sFound = true;
|
|
closePositionInBlock = start;
|
|
closeLengthInBlock = length;
|
|
maxOffset = offset;
|
|
break;
|
|
}
|
|
closePos.append(start);
|
|
closeLength.append(length);
|
|
}
|
|
} while(start>=0);
|
|
if (!sFound) break;
|
|
offset = 0; start = -1;
|
|
do {
|
|
iterations++;
|
|
if (iterations > SEARCH_LIMIT) break;
|
|
QRegularExpressionMatch match = openTagExpr.match(blockText, offset);
|
|
start = match.capturedStart();
|
|
if (start>=0) {
|
|
int length = match.capturedLength();
|
|
if (start>=maxOffset) break;
|
|
offset = start + length;
|
|
openPos.append(start);
|
|
openLength.append(length);
|
|
}
|
|
} while(start>=0);
|
|
int openI = 0, closeI = 0;
|
|
if (openPos.size()==0) openI = -1;
|
|
if (closePos.size()==0) closeI = -1;
|
|
while(openI >= 0 || closeI >= 0) {
|
|
int open = -1, close = -1;
|
|
if (openI >= 0) open = openPos.at(openI);
|
|
if (closeI >= 0) close = closePos.at(closeI);
|
|
if (open >= 0 && (open < close || close < 0)) {
|
|
sortedPos.append(open);
|
|
sortedLength.append(openLength.at(openI));
|
|
sortedTag.append(1);
|
|
openI++;
|
|
if (openI>=openPos.size()) openI = -1;
|
|
} else if (close >= 0 && (open > close || open < 0)) {
|
|
sortedPos.append(close);
|
|
sortedLength.append(closeLength.at(closeI));
|
|
sortedTag.append(-1);
|
|
closeI++;
|
|
if (closeI>=closePos.size()) closeI = -1;
|
|
}
|
|
}
|
|
if (sortedPos.size() != openPos.size()+closePos.size()) break;
|
|
for (int i=sortedPos.size()-1; i>=0; i--) {
|
|
int tag = sortedTag.at(i);
|
|
if (tag < 0) {
|
|
count++;
|
|
} else if (tag > 0 && count > 0) {
|
|
count--;
|
|
} else if (tag > 0 && count == 0) {
|
|
positionInBlock = sortedPos.at(i);
|
|
lengthInBlock = sortedLength.at(i);
|
|
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, positionInBlock);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, lengthInBlock);
|
|
QTextEdit::ExtraSelection charSelection;
|
|
charSelection.format.setBackground(selectedExpressionBgColor);
|
|
charSelection.cursor = cursor;
|
|
extraSelections->append(charSelection);
|
|
|
|
QTextCursor cursorPair = textCursor();
|
|
cursorPair.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursorPair.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, closePositionInBlock);
|
|
cursorPair.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, closeLengthInBlock);
|
|
QTextEdit::ExtraSelection charSelectionPair;
|
|
charSelectionPair.format.setBackground(selectedExpressionBgColor);
|
|
charSelectionPair.cursor = cursorPair;
|
|
extraSelections->append(charSelectionPair);
|
|
|
|
// line mark
|
|
if (cursor.block().blockNumber() < cursorPair.block().blockNumber()) {
|
|
for (int i=cursor.block().blockNumber(); i<=cursorPair.block().blockNumber(); i++) {
|
|
static_cast<LineMark *>(lineMark)->addMark(i+1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) break;
|
|
blockText = cursor.block().text();
|
|
maxOffset = blockText.size();
|
|
} while(positionInBlock < 0);
|
|
}
|
|
|
|
void Editor::highlightPHPOpenSpecialTagPair(QString tagName, int pos, QList<QTextEdit::ExtraSelection> * extraSelections)
|
|
{
|
|
QRegularExpression openTagExpr, closeTagExpr;
|
|
if (tagName == "if") {
|
|
openTagExpr = tagExprIf;
|
|
closeTagExpr = tagExprEndif;
|
|
} else if (tagName == "switch") {
|
|
openTagExpr = tagExprSwitch;
|
|
closeTagExpr = tagExprEndswitch;
|
|
} else if (tagName == "while") {
|
|
openTagExpr = tagExprWhile;
|
|
closeTagExpr = tagExprEndwhile;
|
|
} else if (tagName == "for") {
|
|
openTagExpr = tagExprFor;
|
|
closeTagExpr = tagExprEndfor;
|
|
} else if (tagName == "foreach") {
|
|
openTagExpr = tagExprForeach;
|
|
closeTagExpr = tagExprEndforeach;
|
|
} else {
|
|
return;
|
|
}
|
|
QTextCursor cursor = textCursor();
|
|
QString blockText = cursor.block().text();
|
|
bool sFound = false;
|
|
int minOffset = 0;
|
|
int count = 0;
|
|
int positionInBlock = -1;
|
|
int lengthInBlock = 0;
|
|
int openPositionInBlock = -1;
|
|
int openLengthInBlock = 0;
|
|
int iterations = 0;
|
|
do {
|
|
QVector<int> openPos, openLength, closePos, closeLength, sortedPos, sortedLength, sortedTag;
|
|
int offset = 0, start = -1;
|
|
do {
|
|
iterations++;
|
|
if (iterations > SEARCH_LIMIT) break;
|
|
QRegularExpressionMatch match = openTagExpr.match(blockText, offset);
|
|
start = match.capturedStart();
|
|
if (start>=0) {
|
|
int length = match.capturedLength();
|
|
offset = start + length;
|
|
if (!sFound && start<=pos && start+length>=pos) {
|
|
sFound = true;
|
|
openPositionInBlock = start;
|
|
openLengthInBlock = length;
|
|
minOffset = offset;
|
|
} else if (sFound) {
|
|
openPos.append(start);
|
|
openLength.append(length);
|
|
}
|
|
}
|
|
} while(start>=0);
|
|
if (!sFound) break;
|
|
offset = 0; start = -1;
|
|
do {
|
|
iterations++;
|
|
if (iterations > SEARCH_LIMIT) break;
|
|
QRegularExpressionMatch match = closeTagExpr.match(blockText, offset);
|
|
start = match.capturedStart();
|
|
if (start>=0) {
|
|
int length = match.capturedLength();
|
|
offset = start + length;
|
|
if (start<minOffset) continue;
|
|
closePos.append(start);
|
|
closeLength.append(length);
|
|
}
|
|
} while(start>=0);
|
|
int openI = 0, closeI = 0;
|
|
if (openPos.size()==0) openI = -1;
|
|
if (closePos.size()==0) closeI = -1;
|
|
while(openI >= 0 || closeI >= 0) {
|
|
int open = -1, close = -1;
|
|
if (openI >= 0) open = openPos.at(openI);
|
|
if (closeI >= 0) close = closePos.at(closeI);
|
|
if (open >= 0 && (open < close || close < 0)) {
|
|
sortedPos.append(open);
|
|
sortedLength.append(openLength.at(openI));
|
|
sortedTag.append(1);
|
|
openI++;
|
|
if (openI>=openPos.size()) openI = -1;
|
|
} else if (close >= 0 && (open > close || open < 0)) {
|
|
sortedPos.append(close);
|
|
sortedLength.append(closeLength.at(closeI));
|
|
sortedTag.append(-1);
|
|
closeI++;
|
|
if (closeI>=closePos.size()) closeI = -1;
|
|
}
|
|
}
|
|
if (sortedPos.size() != openPos.size()+closePos.size()) break;
|
|
for (int i=0; i<sortedPos.size(); i++) {
|
|
int tag = sortedTag.at(i);
|
|
if (tag > 0) {
|
|
count++;
|
|
} else if (tag < 0 && count > 0) {
|
|
count--;
|
|
} else if (tag < 0 && count == 0) {
|
|
positionInBlock = sortedPos.at(i);
|
|
lengthInBlock = sortedLength.at(i);
|
|
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, positionInBlock);
|
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, lengthInBlock);
|
|
QTextEdit::ExtraSelection charSelection;
|
|
charSelection.format.setBackground(selectedExpressionBgColor);
|
|
charSelection.cursor = cursor;
|
|
extraSelections->append(charSelection);
|
|
|
|
QTextCursor cursorPair = textCursor();
|
|
cursorPair.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
cursorPair.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, openPositionInBlock);
|
|
cursorPair.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, openLengthInBlock);
|
|
QTextEdit::ExtraSelection charSelectionPair;
|
|
charSelectionPair.format.setBackground(selectedExpressionBgColor);
|
|
charSelectionPair.cursor = cursorPair;
|
|
extraSelections->append(charSelectionPair);
|
|
|
|
// line mark
|
|
if (cursor.block().blockNumber() > cursorPair.block().blockNumber()) {
|
|
for (int i=cursorPair.block().blockNumber(); i<=cursor.block().blockNumber(); i++) {
|
|
static_cast<LineMark *>(lineMark)->addMark(i+1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor)) break;
|
|
blockText = cursor.block().text();
|
|
minOffset = 0;
|
|
} while(positionInBlock < 0);
|
|
}
|
|
|
|
void Editor::resetExtraSelections()
|
|
{
|
|
QList<QTextEdit::ExtraSelection> extraSelections;
|
|
highlightCurrentLine(& extraSelections);
|
|
setExtraSelections(extraSelections);
|
|
}
|
|
|
|
void Editor::highlightCurrentLine(QList<QTextEdit::ExtraSelection> * extraSelections)
|
|
{
|
|
QTextEdit::ExtraSelection selectedLineSelection;
|
|
selectedLineSelection.format.setBackground(selectedLineBgColor);
|
|
selectedLineSelection.format.setProperty(QTextFormat::FullWidthSelection, true);
|
|
selectedLineSelection.cursor = textCursor();
|
|
selectedLineSelection.cursor.clearSelection();
|
|
if (selectedLineSelection.cursor.block().layout() != nullptr && selectedLineSelection.cursor.block().layout()->lineCount() > 1) {
|
|
selectedLineSelection.cursor.movePosition(QTextCursor::StartOfBlock);
|
|
selectedLineSelection.cursor.movePosition(QTextCursor::NextRow, QTextCursor::KeepAnchor);
|
|
}
|
|
extraSelections->append(selectedLineSelection);
|
|
}
|
|
|
|
void Editor::highlightMultiSelection(QList<QTextEdit::ExtraSelection> * extraSelections)
|
|
{
|
|
if (isMultiSelectMode && multiSelectString.size() > 0) {
|
|
int searchWordBlockNumber = -1;
|
|
QTextCursor curs = textCursor();
|
|
if (multiSelectCursor.hasSelection()) curs = multiSelectCursor;
|
|
else {
|
|
curs.setPosition(multiSelectCursor.position());
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, multiSelectInsertText.length());
|
|
}
|
|
QTextEdit::ExtraSelection selectedWordSelection;
|
|
selectedWordSelection.format.setBackground(selectionBgColor);
|
|
selectedWordSelection.format.setForeground(selectionColor);
|
|
selectedWordSelection.cursor = curs;
|
|
extraSelections->append(selectedWordSelection);
|
|
if (searchWordBlockNumber != curs.block().blockNumber()) {
|
|
searchWordBlockNumber = curs.block().blockNumber();
|
|
static_cast<LineMap *>(lineMap)->addMark(searchWordBlockNumber+1);
|
|
}
|
|
for (QTextCursor cursor : multiSelectCursors) {
|
|
QTextCursor curs = textCursor();
|
|
if (cursor.hasSelection()) curs = cursor;
|
|
else {
|
|
curs.setPosition(cursor.position());
|
|
curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, multiSelectInsertText.length());
|
|
}
|
|
QTextEdit::ExtraSelection selectedWordSelection;
|
|
selectedWordSelection.format.setBackground(selectionBgColor);
|
|
selectedWordSelection.format.setForeground(selectionColor);
|
|
selectedWordSelection.cursor = curs;
|
|
extraSelections->append(selectedWordSelection);
|
|
if (searchWordBlockNumber != curs.block().blockNumber()) {
|
|
searchWordBlockNumber = curs.block().blockNumber();
|
|
static_cast<LineMap *>(lineMap)->addMark(searchWordBlockNumber+1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::highlightSearchWords(QList<QTextEdit::ExtraSelection> *extraSelections)
|
|
{
|
|
if (!search->isVisible() || searchString.size() == 0) return;
|
|
static_cast<LineMap *>(lineMap)->clearMarks();
|
|
QTextCursor searchWordCursor(document());
|
|
QTextDocument::FindFlags findFlags;
|
|
if (searchCaSe) findFlags |= QTextDocument::FindCaseSensitively;
|
|
if (searchWord) findFlags |= QTextDocument::FindWholeWords;
|
|
searchWordCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
int searchWordBlockNumber = -1;
|
|
int co = 0;
|
|
while(!searchWordCursor.isNull() && !searchWordCursor.atEnd()) {
|
|
if (!searchRegE) searchWordCursor = document()->find(searchString, searchWordCursor, findFlags);
|
|
else searchWordCursor = document()->find(QRegularExpression(searchString), searchWordCursor, findFlags);
|
|
if (!searchWordCursor.isNull()) {
|
|
QTextEdit::ExtraSelection selectedWordSelection;
|
|
selectedWordSelection.format.setBackground(searchWordBgColor);
|
|
selectedWordSelection.format.setForeground(searchWordColor);
|
|
selectedWordSelection.cursor = searchWordCursor;
|
|
extraSelections->append(selectedWordSelection);
|
|
if (searchWordBlockNumber != searchWordCursor.block().blockNumber()) {
|
|
searchWordBlockNumber = searchWordCursor.block().blockNumber();
|
|
static_cast<LineMap *>(lineMap)->addMark(searchWordBlockNumber+1);
|
|
}
|
|
}
|
|
co++;
|
|
if (co >= SEARCH_LIMIT) break; // too much results
|
|
}
|
|
}
|
|
|
|
void Editor::highlightExtras(QChar prevChar, QChar nextChar, QChar cursorTextPrevChar, QString cursorText, int cursorTextPos, std::string mode)
|
|
{
|
|
QList<QTextEdit::ExtraSelection> extraSelections;
|
|
if (!isReadOnly()) {
|
|
static_cast<LineMark *>(lineMark)->clearMarks();
|
|
// current line
|
|
highlightCurrentLine(& extraSelections);
|
|
|
|
// word under cursor
|
|
if (cursorText.size()>0) {
|
|
if (mode == MODE_PHP && (cursorText == "if" || cursorText == "switch" || cursorText == "while" || cursorText == "for" || cursorText == "foreach")) {
|
|
highlightPHPOpenSpecialTagPair(cursorText, cursorTextPos, & extraSelections);
|
|
} else if (mode == MODE_PHP && (cursorText == "endif" || cursorText == "else" || cursorText == "endswitch" || cursorText == "case" || cursorText == "endwhile" || cursorText == "endfor" || cursorText == "endforeach")) {
|
|
highlightPHPCloseSpecialTagPair(cursorText, cursorTextPos, & extraSelections);
|
|
} else if (mode == MODE_HTML && cursorTextPrevChar == "<") {
|
|
highlightOpenTagPair(cursorText, cursorTextPos, & extraSelections);
|
|
} else if (mode == MODE_HTML && cursorTextPrevChar == "/") {
|
|
highlightCloseTagPair(cursorText, cursorTextPos, & extraSelections);
|
|
} else if (!search->isVisible()) {
|
|
QTextCursor selectedWordCursor(document());
|
|
QTextDocument::FindFlags findFlags = QTextDocument::FindCaseSensitively;
|
|
if (!textCursor().hasSelection()) findFlags |= QTextDocument::FindWholeWords;
|
|
selectedWordCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
int iterator = 0;
|
|
while(!selectedWordCursor.isNull() && !selectedWordCursor.atEnd()) {
|
|
iterator++;
|
|
if (iterator > SEARCH_LIMIT) break;
|
|
selectedWordCursor = document()->find(cursorText, selectedWordCursor, findFlags);
|
|
if (!selectedWordCursor.isNull()) {
|
|
QTextEdit::ExtraSelection selectedWordSelection;
|
|
selectedWordSelection.format.setBackground(selectedWordBgColor);
|
|
selectedWordSelection.cursor = selectedWordCursor;
|
|
extraSelections.append(selectedWordSelection);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// search words
|
|
highlightSearchWords(& extraSelections);
|
|
|
|
// multiselect
|
|
highlightMultiSelection(& extraSelections);
|
|
|
|
// repaint line map
|
|
lineMap->update();
|
|
|
|
// braces
|
|
if (prevChar=="}") {
|
|
highlightCloseCharPair('{', '}', & extraSelections);
|
|
}
|
|
if (nextChar=="{") {
|
|
highlightOpenCharPair('{', '}', & extraSelections);
|
|
}
|
|
|
|
// parentheses
|
|
if (prevChar==")") {
|
|
highlightCloseCharPair('(', ')', & extraSelections);
|
|
}
|
|
if (nextChar=="(") {
|
|
highlightOpenCharPair('(', ')', & extraSelections);
|
|
}
|
|
|
|
// square brackets
|
|
if (prevChar=="]") {
|
|
highlightCloseCharPair('[', ']', & extraSelections);
|
|
}
|
|
if (nextChar=="[") {
|
|
highlightOpenCharPair('[', ']', & extraSelections);
|
|
}
|
|
|
|
// repaint line mark
|
|
lineMark->update();
|
|
|
|
// hovered words
|
|
for (int i=0; i<wordsExtraSelections.size(); i++) {
|
|
extraSelections.append(wordsExtraSelections.at(i));
|
|
}
|
|
|
|
// errors
|
|
for (int i=0; i<errorsExtraSelections.size(); i++) {
|
|
extraSelections.append(errorsExtraSelections.at(i));
|
|
}
|
|
}
|
|
setExtraSelections(extraSelections);
|
|
}
|
|
|
|
void Editor::clearWarnings()
|
|
{
|
|
static_cast<LineMap *>(lineMap)->clearWarnings();
|
|
static_cast<LineMark *>(lineMark)->clearWarnings();
|
|
}
|
|
|
|
void Editor::setWarning(int line, QString text)
|
|
{
|
|
static_cast<LineMap *>(lineMap)->addWarning(line);
|
|
static_cast<LineMark *>(lineMark)->addWarning(line, text);
|
|
}
|
|
|
|
void Editor::clearErrors()
|
|
{
|
|
static_cast<LineMap *>(lineMap)->clearErrors();
|
|
static_cast<LineMark *>(lineMark)->clearErrors();
|
|
breadcrumbs->setToolTip("");
|
|
clearErrorsFormat();
|
|
}
|
|
|
|
void Editor::setError(int line, QString text)
|
|
{
|
|
static_cast<LineMap *>(lineMap)->addError(line);
|
|
static_cast<LineMark *>(lineMark)->addError(line, text);
|
|
breadcrumbs->setToolTip(text+" ["+tr("Line")+": "+Helper::intToStr(line)+"]");
|
|
}
|
|
|
|
void Editor::updateMarksAndMapArea()
|
|
{
|
|
lineMark->update();
|
|
lineMap->update();
|
|
breadcrumbs->update();
|
|
}
|
|
|
|
void Editor::findToggle()
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
/*
|
|
if (!search->isVisible() || curs.hasSelection()) {
|
|
if (curs.hasSelection()) {
|
|
searchString = curs.selectedText();
|
|
static_cast<Search *>(search)->setFindEditText(searchString);
|
|
}
|
|
showSearch();
|
|
highlightExtras();
|
|
} else {
|
|
closeSearch();
|
|
}
|
|
*/
|
|
if (curs.hasSelection()) {
|
|
searchString = curs.selectedText();
|
|
static_cast<Search *>(search)->setFindEditText(searchString);
|
|
}
|
|
showSearch();
|
|
highlightExtras();
|
|
}
|
|
|
|
void Editor::showSearch()
|
|
{
|
|
if (!search->isVisible()) search->show();
|
|
static_cast<Search *>(search)->setFindEditFocus();
|
|
if (!searchDisplayOnTop) {
|
|
static_cast<Search *>(search)->updateScrollBar();
|
|
}
|
|
updateViewportMargins();
|
|
}
|
|
|
|
void Editor::closeSearch()
|
|
{
|
|
if (!search->isVisible()) return;
|
|
search->hide();
|
|
if (!searchDisplayOnTop) {
|
|
static_cast<Search *>(search)->updateScrollBar();
|
|
}
|
|
updateViewportMargins();
|
|
setFocus();
|
|
static_cast<LineMap *>(lineMap)->clearMarks();
|
|
highlightExtras();
|
|
}
|
|
|
|
void Editor::searchFlagsChanged(QString searchStr, bool CaSe, bool Word, bool RegE)
|
|
{
|
|
searchString = searchStr;
|
|
searchCaSe = CaSe;
|
|
searchWord = Word;
|
|
searchRegE = RegE;
|
|
highlightExtras();
|
|
}
|
|
|
|
void Editor::searchText(QString searchTxt, bool CaSe, bool Word, bool RegE, bool backwards, bool fromStart)
|
|
{
|
|
searchString = searchTxt;
|
|
searchCaSe = CaSe;
|
|
searchWord = Word;
|
|
searchRegE = RegE;
|
|
if (searchTxt.size() == 0) return;
|
|
QTextCursor curs = textCursor();
|
|
if (curs.hasSelection()) {
|
|
int startSel = curs.selectionStart();
|
|
int endSel = curs.selectionEnd();
|
|
curs.clearSelection();
|
|
if (!backwards) curs.setPosition(endSel);
|
|
else curs.setPosition(startSel);
|
|
}
|
|
if (fromStart) {
|
|
if (!backwards) curs.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
else curs.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
|
}
|
|
if (backwards && curs.positionInBlock() == 0) curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor); // bug ?
|
|
QTextDocument::FindFlags flags;
|
|
if (CaSe) flags |= QTextDocument::FindCaseSensitively;
|
|
if (Word) flags |= QTextDocument::FindWholeWords;
|
|
if (backwards) flags |= QTextDocument::FindBackward;
|
|
QTextCursor resCurs;
|
|
if (!RegE) {
|
|
resCurs = document()->find(searchTxt, curs, flags);
|
|
} else {
|
|
resCurs = document()->find(QRegularExpression(searchTxt), curs, flags);
|
|
}
|
|
if (!resCurs.isNull()) {
|
|
setTextCursor(resCurs);
|
|
static_cast<Search *>(search)->setFindEditBg(searchInputBgColor);
|
|
static_cast<Search *>(search)->setFindEditProp("results", "found");
|
|
if (fromStart && !backwards) {
|
|
emit showPopupText(tabIndex, tr("Searching from the beginning..."));
|
|
} else if (fromStart && backwards) {
|
|
emit showPopupText(tabIndex, tr("Searching from the end..."));
|
|
}
|
|
} else {
|
|
if (!fromStart) return searchText(searchTxt, CaSe, Word, RegE, backwards, true);
|
|
else {
|
|
static_cast<Search *>(search)->setFindEditBg(searchInputErrorBgColor);
|
|
static_cast<Search *>(search)->setFindEditProp("results", "notfound");
|
|
}
|
|
}
|
|
highlightExtras();
|
|
}
|
|
|
|
void Editor::replaceText(QString searchTxt, QString replaceTxt, bool CaSe, bool Word, bool RegE)
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
if (curs.hasSelection()) {
|
|
curs.insertText(replaceTxt);
|
|
setTextCursor(curs);
|
|
} else {
|
|
searchText(searchTxt, CaSe, Word, RegE);
|
|
}
|
|
}
|
|
|
|
void Editor::replaceAllText(QString searchTxt, QString replaceTxt, bool CaSe, bool Word, bool RegE)
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
if (curs.hasSelection()) searchTxt = curs.selectedText();
|
|
searchString = searchTxt;
|
|
searchCaSe = CaSe;
|
|
searchWord = Word;
|
|
searchRegE = RegE;
|
|
if (searchTxt.size() == 0) return;
|
|
curs.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
curs.beginEditBlock();
|
|
QTextDocument::FindFlags flags;
|
|
if (CaSe) flags |= QTextDocument::FindCaseSensitively;
|
|
if (Word) flags |= QTextDocument::FindWholeWords;
|
|
QTextCursor resCurs;
|
|
int co = 0;
|
|
do {
|
|
if (!RegE) {
|
|
resCurs = document()->find(searchTxt, curs, flags);
|
|
} else {
|
|
resCurs = document()->find(QRegularExpression(searchTxt), curs, flags);
|
|
}
|
|
if (!resCurs.isNull()) {
|
|
curs.setPosition(resCurs.selectionStart(), QTextCursor::MoveAnchor);
|
|
curs.setPosition(resCurs.selectionEnd(), QTextCursor::KeepAnchor);
|
|
curs.insertText(replaceTxt);
|
|
// modified state
|
|
modifiedLinesIterator = modifiedLines.find(curs.block().blockNumber() + 1);
|
|
if (modifiedLinesIterator == modifiedLines.end()) {
|
|
modifiedLines[curs.block().blockNumber() + 1] = curs.block().blockNumber() + 1;
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(curs.block().userData());
|
|
if (blockData != nullptr) {
|
|
blockData->isModified = true;
|
|
curs.block().setUserData(blockData);
|
|
}
|
|
}
|
|
co++;
|
|
}
|
|
if (co > SEARCH_LIMIT) {
|
|
emit showPopupText(tabIndex, QObject::tr("Too many results. Breaking..."));
|
|
break;
|
|
}
|
|
} while(!resCurs.isNull());
|
|
curs.endEditBlock();
|
|
if (co > 0) {
|
|
setTextCursor(curs);
|
|
static_cast<Search *>(search)->setFindEditBg(searchInputBgColor);
|
|
static_cast<Search *>(search)->setFindEditProp("results", "found");
|
|
} else {
|
|
static_cast<Search *>(search)->setFindEditBg(searchInputErrorBgColor);
|
|
static_cast<Search *>(search)->setFindEditProp("results", "notfound");
|
|
}
|
|
lineNumber->update();
|
|
highlightExtras();
|
|
}
|
|
|
|
void Editor::selectWord()
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
if (curs.hasSelection()) return;
|
|
curs.select(QTextCursor::WordUnderCursor);
|
|
setTextCursor(curs);
|
|
}
|
|
|
|
void Editor::multiSelectToggle()
|
|
{
|
|
selectWord();
|
|
#if defined(Q_OS_ANDROID)
|
|
emit showPopupText(tabIndex, tr("Multi-Selection is not supported"));
|
|
return;
|
|
#endif
|
|
QTextCursor curs = textCursor();
|
|
if (!isMultiSelectMode && curs.hasSelection() && curs.selectedText().replace(QString::fromWCharArray(L"\u2029"),"\n").indexOf("\n") < 0) {
|
|
isMultiSelectMode = true;
|
|
multiSelectString = curs.selectedText();
|
|
multiSelectInsertText = "";
|
|
multiSelectCursor.setPosition(curs.selectionStart());
|
|
multiSelectCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, curs.selectionEnd() - curs.selectionStart());
|
|
curs.clearSelection();
|
|
setTextCursor(curs);
|
|
|
|
QTextCursor searchWordCursor(document());
|
|
QTextDocument::FindFlags findFlags;
|
|
findFlags |= QTextDocument::FindCaseSensitively;
|
|
searchWordCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
int co = 0;
|
|
while(!searchWordCursor.isNull() && !searchWordCursor.atEnd()) {
|
|
searchWordCursor = document()->find(multiSelectString, searchWordCursor, findFlags);
|
|
if (!searchWordCursor.isNull() && searchWordCursor.position() != multiSelectCursor.position()) {
|
|
multiSelectCursors.append(searchWordCursor);
|
|
}
|
|
co++;
|
|
if (co >= SEARCH_LIMIT) break; // too much results
|
|
}
|
|
} else {
|
|
isMultiSelectMode = false;
|
|
multiSelectString = "";
|
|
multiSelectInsertText = "";
|
|
multiSelectCursors.clear();
|
|
}
|
|
static_cast<LineMap *>(lineMap)->clearMarks();
|
|
highlightExtras();
|
|
}
|
|
|
|
void Editor::scrollLineMap(int y)
|
|
{
|
|
if (y < 0 || !verticalScrollBar()->isVisible()) return;
|
|
int height = lineMap->geometry().height();
|
|
if (verticalScrollBar()->geometry().height() < height) {
|
|
height = verticalScrollBar()->geometry().height();
|
|
}
|
|
if (height <= 0) return;
|
|
if (y > height) y = height;
|
|
int p = (y * verticalScrollBar()->maximum()) / height;
|
|
hideTooltip();
|
|
verticalScrollBar()->setValue(p);
|
|
}
|
|
|
|
int Editor::getCursorLine()
|
|
{
|
|
QTextCursor cursor = textCursor();
|
|
return cursor.blockNumber() + 1;
|
|
}
|
|
|
|
void Editor::gotoLine(int line, bool focus) {
|
|
QTextCursor cursor = textCursor();
|
|
cursor.movePosition(QTextCursor::Start);
|
|
if (line > 1) cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, line-1);
|
|
QString blockText = cursor.block().text();
|
|
QFontMetrics fm(font());
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
if (fm.width(blockText) > viewport()->width()) {
|
|
*/
|
|
if (fm.horizontalAdvance(blockText) > viewport()->width()) {
|
|
do {
|
|
int pos = cursor.positionInBlock();
|
|
if (pos >= blockText.size()) break;
|
|
QChar c = blockText[pos];
|
|
if (!c.isSpace()) break;
|
|
} while(cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor));
|
|
} else {
|
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
}
|
|
setTextCursor(cursor);
|
|
if (focus) setFocus();
|
|
scrollToMiddle(cursor, line);
|
|
}
|
|
|
|
void Editor::gotoLineSymbol(int line, int symbol) {
|
|
QTextCursor cursor = textCursor();
|
|
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
|
if (symbol >= 0 && symbol <= cursor.position()) {
|
|
cursor.setPosition(symbol);
|
|
setTextCursor(cursor);
|
|
setFocus();
|
|
scrollToMiddle(cursor, line);
|
|
}
|
|
}
|
|
|
|
void Editor::showLineNumber(int y)
|
|
{
|
|
if (gitAnnotations.size() == 0) return;
|
|
if (y < 0) return;
|
|
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
|
|
if (!(modifiers & Qt::ControlModifier)) return;
|
|
int blockNumber = getFirstVisibleBlockIndex();
|
|
if (blockNumber < 0) return;
|
|
if (blockNumber>0) blockNumber--;
|
|
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
//int top = contentsMargins().top();
|
|
int top = 0; // widget has offset
|
|
|
|
if (blockNumber == 0) {
|
|
top += static_cast<int>(document()->documentMargin()) - verticalScrollBar()->sliderPosition();
|
|
} else {
|
|
QTextBlock prev_block = document()->findBlockByNumber(blockNumber-1);
|
|
int prev_y = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).y());
|
|
int prev_h = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).height());
|
|
top += prev_y + prev_h - verticalScrollBar()->sliderPosition();
|
|
}
|
|
|
|
int bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
|
|
QString tooltipText = "";
|
|
while (block.isValid() && top < y) {
|
|
if (block.isVisible() && top < y && bottom > y) {
|
|
int line = blockNumber + 1;
|
|
if (gitAnnotations.contains(line)) {
|
|
Git::Annotation annotation = gitAnnotations.value(line);
|
|
tooltipText = tr("Line") + " " + Helper::intToStr(annotation.line) + ": " + annotation.comment + "\n" + annotation.committer + " [" + annotation.committerDate + "]";
|
|
}
|
|
break;
|
|
}
|
|
block = block.next();
|
|
top = bottom;
|
|
bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
blockNumber++;
|
|
}
|
|
|
|
if (tooltipText.size() > 0) {
|
|
/*
|
|
QFontMetrics fm(editorTooltipFont);
|
|
int linesCo = tooltipText.split("\n").size();
|
|
if (y < fm.height()*linesCo) y += fm.height()*linesCo;
|
|
showTooltip(lineMark->geometry().x()+lineMark->geometry().width()+TOOLTIP_OFFSET, y+TOOLTIP_OFFSET, tooltipText, false);
|
|
*/
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::Start);
|
|
if (blockNumber > 0) curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, blockNumber);
|
|
showTooltip(&curs, tooltipText, false);
|
|
} else {
|
|
hideTooltip();
|
|
}
|
|
}
|
|
|
|
void Editor::showLineMap(int y)
|
|
{
|
|
if (y < 0 || !verticalScrollBar()->isVisible()) return;
|
|
Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers();
|
|
if (!(modifiers & Qt::ControlModifier)) return;
|
|
int height = lineMap->geometry().height();
|
|
if (verticalScrollBar()->geometry().height() < height) {
|
|
height = verticalScrollBar()->geometry().height();
|
|
}
|
|
if (height <= 0) return;
|
|
if (y > height) y = height;
|
|
int n = (y * document()->blockCount()) / height;
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
QString tooltipText = "";
|
|
int maxChars = 50, blocksCount = document()->blockCount();
|
|
QString blocksCountStr = Helper::intToStr(blocksCount);
|
|
maxChars += blocksCountStr.size()+2;
|
|
QFontMetrics fm(editorTooltipFont);
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
int tooltipWidth = fm.width(QString(" ").repeated(maxChars+3)) + tooltipLabel.contentsMargins().left() + tooltipLabel.contentsMargins().right() + 1;
|
|
*/
|
|
int tooltipWidth = fm.horizontalAdvance(QString(" ").repeated(maxChars+3)) + tooltipLabel.contentsMargins().left() + tooltipLabel.contentsMargins().right() + 1;
|
|
int linesCo = 0;
|
|
do {
|
|
QTextBlock block = curs.block();
|
|
int blockNumber = block.blockNumber();
|
|
if (blockNumber >= n-LINE_MAP_LINE_NUMBER_OFFSET && blockNumber <= n+LINE_MAP_LINE_NUMBER_OFFSET) {
|
|
if (tooltipText.size() > 0) tooltipText += "\n";
|
|
QString lineNumberStr = Helper::intToStr(blockNumber+1);
|
|
if (lineNumberStr.size()<blocksCountStr.size()) lineNumberStr = QString(" ").repeated(blocksCountStr.size() - lineNumberStr.size())+lineNumberStr;
|
|
markPointsIterator = markPoints.find(blockNumber+1);
|
|
if (markPointsIterator != markPoints.end()) lineNumberStr = "> " + lineNumberStr;
|
|
else lineNumberStr = " "+lineNumberStr;
|
|
QString blockText = lineNumberStr+" "+block.text().replace("\t", QString(" ").repeated(tabWidth));
|
|
if (blockText.size()>maxChars) blockText=blockText.mid(0, maxChars)+"...";
|
|
tooltipText += blockText;
|
|
linesCo++;
|
|
}
|
|
if (blockNumber >= n+LINE_MAP_LINE_NUMBER_OFFSET) break;
|
|
} while(curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor));
|
|
if (tooltipText.size() > 0) {
|
|
//if (y < fm.height()*linesCo) y += fm.height()*linesCo;
|
|
//int tY = y+TOOLTIP_OFFSET;
|
|
int mapHeight = lineMap->geometry().height();
|
|
if (verticalScrollBar()->isVisible() && verticalScrollBar()->geometry().height() < mapHeight) mapHeight = verticalScrollBar()->geometry().height();
|
|
int tY = lineMap->geometry().top() + mapHeight - TOOLTIP_OFFSET;
|
|
showTooltip(lineMap->geometry().x()-tooltipWidth-TOOLTIP_OFFSET, tY, tooltipText, false, tooltipWidth);
|
|
} else {
|
|
hideTooltip();
|
|
}
|
|
}
|
|
|
|
void Editor::showLineMark(int y)
|
|
{
|
|
if (y < 0) return;
|
|
int blockNumber = getFirstVisibleBlockIndex();
|
|
if (blockNumber < 0) return;
|
|
if (blockNumber>0) blockNumber--;
|
|
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
//int top = contentsMargins().top();
|
|
int top = 0; // widget has offset
|
|
|
|
if (blockNumber == 0) {
|
|
top += static_cast<int>(document()->documentMargin()) - verticalScrollBar()->sliderPosition();
|
|
} else {
|
|
QTextBlock prev_block = document()->findBlockByNumber(blockNumber-1);
|
|
int prev_y = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).y());
|
|
int prev_h = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).height());
|
|
top += prev_y + prev_h - verticalScrollBar()->sliderPosition();
|
|
}
|
|
|
|
int bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
|
|
QString tooltipText = "";
|
|
while (block.isValid() && top < y) {
|
|
if (block.isVisible() && top < y && bottom > y) {
|
|
int line = blockNumber + 1;
|
|
QString errorText = "", warningText = "", markText = "";
|
|
int error = static_cast<LineMark *>(lineMark)->getError(line, errorText);
|
|
int warning = static_cast<LineMark *>(lineMark)->getWarning(line, warningText);
|
|
int mark = static_cast<LineMark *>(lineMark)->getMark(line, markText);
|
|
if (error > 0 || warning > 0 || mark > 0) {
|
|
if (error > 0 && errorText.size() > 0) {
|
|
tooltipText = errorText.replace("\t", QString(" ").repeated(tabWidth));
|
|
} else if (warning > 0 && warningText.size() > 0) {
|
|
tooltipText = warningText.replace("\t", QString(" ").repeated(tabWidth));
|
|
} else if (mark > 0 && markText.size() > 0) {
|
|
tooltipText = markText.replace("\t", QString(" ").repeated(tabWidth));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
block = block.next();
|
|
top = bottom;
|
|
bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
blockNumber++;
|
|
}
|
|
|
|
if (tooltipText.size() > 0) {
|
|
/*
|
|
QFontMetrics fm(editorTooltipFont);
|
|
int linesCo = tooltipText.split("\n").size();
|
|
if (y < fm.height()*linesCo) y += fm.height()*linesCo;
|
|
showTooltip(lineMark->geometry().x()+lineMark->geometry().width()+TOOLTIP_OFFSET, y+TOOLTIP_OFFSET, tooltipText, false);
|
|
*/
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::Start);
|
|
if (blockNumber > 0) curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, blockNumber);
|
|
showTooltip(&curs, tooltipText, false);
|
|
} else {
|
|
hideTooltip();
|
|
}
|
|
}
|
|
|
|
void Editor::addLineMark(int y)
|
|
{
|
|
if (y < 0) return;
|
|
int blockNumber = getFirstVisibleBlockIndex();
|
|
if (blockNumber < 0) return;
|
|
if (blockNumber>0) blockNumber--;
|
|
|
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
|
//int top = contentsMargins().top();
|
|
int top = 0; // widget has offset
|
|
|
|
if (blockNumber == 0) {
|
|
top += static_cast<int>(document()->documentMargin()) - verticalScrollBar()->sliderPosition();
|
|
} else {
|
|
QTextBlock prev_block = document()->findBlockByNumber(blockNumber-1);
|
|
int prev_y = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).y());
|
|
int prev_h = static_cast<int>(document()->documentLayout()->blockBoundingRect(prev_block).height());
|
|
top += prev_y + prev_h - verticalScrollBar()->sliderPosition();
|
|
}
|
|
|
|
int bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
|
|
bool changed = false;
|
|
while (block.isValid() && top < y) {
|
|
if (block.isVisible() && top < y && bottom > y) {
|
|
int line = blockNumber + 1;
|
|
QString text = block.text();
|
|
markPointsIterator = markPoints.find(line);
|
|
if (markPointsIterator == markPoints.end()) {
|
|
markPoints[line] = text.toStdString();
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(block.userData());
|
|
if (blockData != nullptr) {
|
|
blockData->hasMarkPoint = true;
|
|
block.setUserData(blockData);
|
|
}
|
|
} else {
|
|
markPoints.erase(markPointsIterator);
|
|
HighlightData * blockData = dynamic_cast<HighlightData *>(block.userData());
|
|
if (blockData != nullptr) {
|
|
blockData->hasMarkPoint = false;
|
|
block.setUserData(blockData);
|
|
}
|
|
}
|
|
changed = true;
|
|
break;
|
|
}
|
|
block = block.next();
|
|
top = bottom;
|
|
bottom = top + static_cast<int>(document()->documentLayout()->blockBoundingRect(block).height());
|
|
blockNumber++;
|
|
}
|
|
|
|
if (changed) updateMarksAndMapArea();
|
|
}
|
|
|
|
QString Editor::getContent()
|
|
{
|
|
QString text = toPlainText();
|
|
if (newLineMode != LF) {
|
|
QString nl = "\n";
|
|
if (newLineMode == CR) nl = "\r";
|
|
if (newLineMode == CRLF) nl = "\r\n";
|
|
text = text.replace("\n", nl);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
void Editor::highlightUnusedVars(bool update)
|
|
{
|
|
if (!experimentalMode) return;
|
|
if (isBigFile || highlight->getFoundModes().contains(QString::fromStdString(MODE_HTML))) return;
|
|
setReadOnly(true);
|
|
QVector<QTextBlock> unusedVarsBlocks;
|
|
highlight->unusedVars.clear();
|
|
// update highlighter
|
|
if (update) {
|
|
highlight->setHighlightVarsMode(true);
|
|
highlight->rehighlight();
|
|
highlight->setHighlightVarsMode(false);
|
|
}
|
|
QStringList knownFunctions = highlight->getKnownFunctions();
|
|
// function vars
|
|
for (int i=0; i<knownFunctions.size(); i++) {
|
|
QString funcKey = knownFunctions.at(i);
|
|
QString clsName = "", funcName = "";
|
|
if (funcKey.indexOf("::") >= 0) {
|
|
clsName = funcKey.mid(0, funcKey.indexOf("::"));
|
|
funcName = funcKey.mid(funcKey.indexOf("::")+2);
|
|
} else {
|
|
funcName = funcKey;
|
|
}
|
|
if (funcName.size() == 0) continue;
|
|
QStringList vars = highlight->getKnownVars(clsName, funcName);
|
|
QStringList used = highlight->getUsedVars(clsName, funcName);
|
|
QString usedChain = used.join(",") + ",";
|
|
for (int i=0; i<vars.size(); i++) {
|
|
QString varName =vars.at(i);
|
|
if (usedChain.indexOf(varName + ",") < 0) {
|
|
int pos = highlight->getKnownVarPosition(clsName, funcName, varName);
|
|
int blockNumber = highlight->getKnownVarBlockNumber(clsName, funcName, varName);
|
|
if (pos >= 0 && blockNumber >= 0) {
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::Start);
|
|
if (blockNumber > 0) curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, blockNumber);
|
|
QString k = clsName + "::" + funcName + "::" + varName;
|
|
highlight->unusedVars[k.toStdString()] = pos;
|
|
unusedVarsBlocks.append(curs.block());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// global vars
|
|
QStringList vars = highlight->getKnownVars("", "");
|
|
QStringList used = highlight->getUsedVars("", "");
|
|
QString usedChain = used.join(",") + ",";
|
|
for (int i=0; i<vars.size(); i++) {
|
|
QString varName =vars.at(i);
|
|
if (usedChain.indexOf(varName + ",") < 0) {
|
|
int pos = highlight->getKnownVarPosition("", "", varName);
|
|
int blockNumber = highlight->getKnownVarBlockNumber("", "", varName);
|
|
if (pos >= 0 && blockNumber >= 0) {
|
|
QTextCursor curs = textCursor();
|
|
curs.movePosition(QTextCursor::Start);
|
|
if (blockNumber > 0) curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, blockNumber);
|
|
QString k = "::::" + varName;
|
|
highlight->unusedVars[k.toStdString()] = pos;
|
|
unusedVarsBlocks.append(curs.block());
|
|
}
|
|
}
|
|
}
|
|
// highlight
|
|
blockSignals(true);
|
|
std::unordered_map<int, int> highlightedBlocks;
|
|
std::unordered_map<int, int>::iterator highlightedBlocksIterator;
|
|
for (int i=0; i<unusedVarsBlocks.size(); i++) {
|
|
QTextBlock block = unusedVarsBlocks.at(i);
|
|
highlightedBlocksIterator = highlightedBlocks.find(block.blockNumber());
|
|
if (highlightedBlocksIterator != highlightedBlocks.end()) continue;
|
|
highlight->rehighlightBlock(block);
|
|
highlightedBlocks[block.blockNumber()] = 1;
|
|
}
|
|
blockSignals(false);
|
|
setReadOnly(false);
|
|
}
|
|
|
|
void Editor::cleanForSave()
|
|
{
|
|
if (!cleanBeforeSave) return;
|
|
QTextCursor curs = textCursor();
|
|
curs.beginEditBlock();
|
|
curs.movePosition(QTextCursor::Start);
|
|
do {
|
|
QString blockText = curs.block().text();
|
|
if (blockText.size() == 0) continue;
|
|
curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
do {
|
|
int pos = curs.positionInBlock();
|
|
if (pos <= 0) break;
|
|
QChar c = blockText.at(pos-1);
|
|
if (!c.isSpace()) break;
|
|
} while(curs.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor));
|
|
if (curs.hasSelection()) {
|
|
curs.deleteChar();
|
|
curs.clearSelection();
|
|
}
|
|
} while(curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor));
|
|
curs.endEditBlock();
|
|
}
|
|
|
|
void Editor::save(QString name)
|
|
{
|
|
if (name.size() == 0 && (fileName.size()==0 || !modified)) return;
|
|
bool nameChanged = false;
|
|
if (name.size() > 0) nameChanged = true;
|
|
QFileInfo fInfo(fileName);
|
|
if (name.size() == 0 && !fInfo.exists()) {
|
|
if (!Helper::showQuestion(tr("Save"), tr("File not found. Create new one ?"))) return;
|
|
nameChanged = true;
|
|
}
|
|
QDateTime dtModified = fInfo.lastModified();
|
|
if (name.size() == 0 && fInfo.exists() && dtModified.time().msec() != lastModifiedMsec) {
|
|
if (!Helper::showQuestion(tr("Save"), tr("File was modified externally. Save it anyway ?"))) return;
|
|
}
|
|
cleanForSave();
|
|
QString text = getContent();
|
|
if (name.size() == 0) name = fileName;
|
|
if (!Helper::saveTextFile(name, text, encoding)) {
|
|
Helper::showMessage(QObject::tr("Could not save file. Check file permissions."));
|
|
return;
|
|
}
|
|
setFileName(name);
|
|
modified = false;
|
|
document()->setModified(modified);
|
|
warningDisplayed = false;
|
|
emit modifiedStateChanged(tabIndex, modified);
|
|
emit statusBarText(tabIndex, tr("Saved"));
|
|
if (nameChanged) emit filenameChanged(tabIndex, fileName);
|
|
emit saved(tabIndex);
|
|
if (highlight->getModeType() == MODE_MIXED) highlightUnusedVars(true);
|
|
}
|
|
|
|
void Editor::switchOverwrite()
|
|
{
|
|
if (!overwrite) {
|
|
overwrite = true;
|
|
QFontMetrics fm(editorFont);
|
|
/* QFontMetrics::width is deprecated */
|
|
/*
|
|
setCursorWidth(fm.width(' '));
|
|
*/
|
|
setCursorWidth(fm.horizontalAdvance(" "));
|
|
} else {
|
|
overwrite = false;
|
|
setCursorWidth(1);
|
|
}
|
|
setOverwriteMode(overwrite);
|
|
emit statusBarText(tabIndex, ""); // update status bar
|
|
}
|
|
|
|
void Editor::contextMenuEvent(QContextMenuEvent *event)
|
|
{
|
|
QMenu * menu = createStandardContextMenu();
|
|
menu->setFont(QApplication::font());
|
|
QList<QAction *> actions = menu->actions();
|
|
for (QAction * action : actions) {
|
|
if (action->isSeparator()) continue;
|
|
QString actionName = action->objectName();
|
|
if (actionName.size() == 0) continue;
|
|
QString newActionName = actionName;
|
|
if (actionName == "edit-redo") newActionName = "actionRedo";
|
|
else if (actionName == "edit-undo") newActionName = "actionUndo";
|
|
else if (actionName == "edit-cut") newActionName = "actionCut";
|
|
else if (actionName == "edit-copy") newActionName = "actionCopy";
|
|
else if (actionName == "edit-paste") newActionName = "actionPaste";
|
|
else if (actionName == "edit-delete") newActionName = "actionDelete";
|
|
else continue;
|
|
QIcon icon = Icon::get(newActionName, QIcon(":/icons/"+actionName+".png"));
|
|
if (!icon.isNull()) action->setIcon(icon);
|
|
|
|
}
|
|
menu->addAction(Icon::get("actionRefresh", QIcon(":/icons/view-refresh.png")), tr("Reload"), this, SLOT(reloadRequested()));
|
|
menu->addSeparator();
|
|
menu->addAction(Icon::get("actionFindReplace"), tr("Find \\ Replace"), this, SLOT(showSearchRequested()));
|
|
menu->addAction(Icon::get("actionOpenDeclaration"), tr("Open declaration"), this, SLOT(showDeclarationRequested()));
|
|
menu->addAction(Icon::get("actionSearchInFiles"), tr("Search in files"), this, SLOT(searchInFilesRequested()));
|
|
QAction * multiSelectAction = menu->addAction(Icon::get("actionMultiSelect"), tr("Multi-Selection"), this, SLOT(multiSelectToggle()));
|
|
#if defined(Q_OS_ANDROID)
|
|
multiSelectAction->setEnabled(false);
|
|
#else
|
|
multiSelectAction->setEnabled(true);
|
|
#endif
|
|
menu->addSeparator();
|
|
menu->addAction(Icon::get("actionSave", QIcon(":/icons/document-save.png")), tr("Save"), this, SLOT(save()));
|
|
menu->addSeparator();
|
|
QAction * backAction = menu->addAction(Icon::get("actionBack", QIcon(":/icons/go-previous.png")), tr("Back"), this, SLOT(back()));
|
|
QAction * forwardAction = menu->addAction(Icon::get("actionForward", QIcon(":/icons/go-next.png")), tr("Forward"), this, SLOT(forward()));
|
|
menu->addAction(Icon::get("actionGoto"), tr("Go to ..."), this, SLOT(gotoLineRequest()));
|
|
menu->addSeparator();
|
|
menu->addAction(Icon::get("actionHelp"), tr("Help"), this, SLOT(showHelpRequested()));
|
|
|
|
if (!isBackable()) backAction->setEnabled(false);
|
|
if (!isForwadable()) forwardAction->setEnabled(false);
|
|
|
|
bool useContextDialog = false;
|
|
#if defined(Q_OS_ANDROID)
|
|
if (Settings::get("enable_android_desktop_mode") != "yes") {
|
|
useContextDialog = true;
|
|
}
|
|
#endif
|
|
if (useContextDialog) {
|
|
event->accept();
|
|
Scroller::reset();
|
|
Helper::contextMenuToDialog(menu, this);
|
|
} else {
|
|
menu->exec(event->globalPos());
|
|
}
|
|
|
|
delete menu;
|
|
}
|
|
|
|
void Editor::gotoLineRequest()
|
|
{
|
|
QString text = Helper::showInputDialog(tr("Go to"), tr("Line:"), QLineEdit::Normal);
|
|
if (!text.isNull() && !text.isEmpty()) {
|
|
int line = text.toInt();
|
|
if (line > 0) gotoLine(line);
|
|
}
|
|
}
|
|
|
|
void Editor::showDeclarationRequested()
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
int pos = curs.positionInBlock();
|
|
std::string mode = highlight->findModeAtCursor(&block, pos);
|
|
if (!highlight->isStateOpen(&block, pos)) {
|
|
QChar prevChar = '\0', nextChar = '\0';
|
|
if (pos > 0 && curs.selectedText().size()==0) prevChar = blockText[pos - 1];
|
|
if (pos < total && curs.selectedText().size()==0) nextChar = blockText[pos];
|
|
// text under cursor
|
|
QString cursorText = "", prevText = "", nextText = "";
|
|
bool hasAlpha = false;
|
|
|
|
QChar cursorTextPrevChar = '\0';
|
|
int cursorTextPos = pos;
|
|
if (curs.selectedText().size()==0) {
|
|
for (int i=pos; i>0; i--) {
|
|
QString c = blockText.mid(i-1, 1);
|
|
if (!hasAlpha && isalpha(c[0].toLatin1())) hasAlpha = true;
|
|
cursorTextPrevChar = c[0];
|
|
if (mode == MODE_PHP) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_" || c=="\\") prevText = c + prevText;
|
|
else break;
|
|
} else if (mode == MODE_JS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else {
|
|
if (isalnum(c[0].toLatin1()) || c=="_") prevText = c + prevText;
|
|
else break;
|
|
}
|
|
cursorTextPos = i-1;
|
|
}
|
|
for (int i=pos; i<total; i++) {
|
|
QString c = blockText.mid(i, 1);
|
|
if (!hasAlpha && isalpha(c[0].toLatin1())) hasAlpha = true;
|
|
if (mode == MODE_PHP) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_" || c=="\\") nextText += c;
|
|
else break;
|
|
} else if (mode == MODE_JS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") nextText += c;
|
|
else break;
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") nextText += c;
|
|
else break;
|
|
} else {
|
|
if (isalnum(c[0].toLatin1()) || c=="_") nextText += c;
|
|
else break;
|
|
}
|
|
}
|
|
if (prevText.size()>0 && nextText.size()>0 && hasAlpha) {
|
|
cursorText = prevText + nextText;
|
|
}
|
|
} else {
|
|
cursorText = curs.selectedText();
|
|
int cp = curs.selectionStart();
|
|
curs.clearSelection();
|
|
curs.setPosition(cp);
|
|
if (cursorText.indexOf("::") >= 0) {
|
|
int p = cursorText.lastIndexOf("::");
|
|
cursorText = cursorText.mid(p+2);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, p+2);
|
|
}
|
|
cursorTextPos = curs.positionInBlock();
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
|
|
}
|
|
int goLine = 0;
|
|
QString name = "";
|
|
if (cursorText.size() > 0 && mode == MODE_PHP) {
|
|
name = cursorText;
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QChar prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QChar prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QString nsName = highlight->findNsPHPAtCursor(&block, cursorTextPos);
|
|
QString clsName = highlight->findClsPHPAtCursor(&block, cursorTextPos);
|
|
QString funcName = highlight->findFuncPHPAtCursor(&block, cursorTextPos);
|
|
if ((prevChar == ">" && prevPrevChar == "-") || (prevChar == ":" && prevPrevChar == ":")) {
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QString prevType = detectCompleteTypeAtCursorPHP(curs, nsName, clsName, funcName);
|
|
if (prevType.size() > 0 && prevType.at(0) == "\\") prevType = prevType.mid(1);
|
|
if (prevType.size() > 0) {
|
|
name = prevType + "::" + cursorText;
|
|
}
|
|
}
|
|
if (name.size() > 0) {
|
|
if (name.indexOf("::") < 0) {
|
|
QString ns = "\\";
|
|
if (nsName.size() > 0) ns += nsName + "\\";
|
|
QString _clsName = clsName, _funcName = funcName;
|
|
if (clsName.size() > 0) _clsName = ns + clsName;
|
|
else if (funcName.size() > 0) _funcName = ns + funcName;
|
|
for (int i=0; i<parseResultPHP.functions.size(); i++) {
|
|
ParsePHP::ParseResultFunction _func = parseResultPHP.functions.at(i);
|
|
if (_func.name == ns+name && _func.clsName == _clsName && _func.line > 0 && _func.line-1 != block.blockNumber()) {
|
|
goLine = _func.line;
|
|
}
|
|
}
|
|
if (goLine == 0) {
|
|
for (int i=0; i<parseResultPHP.classes.size(); i++) {
|
|
ParsePHP::ParseResultClass _cls = parseResultPHP.classes.at(i);
|
|
if (_cls.name == ns+name && _cls.line > 0 && _cls.line-1 != block.blockNumber()) {
|
|
goLine = _cls.line;
|
|
}
|
|
}
|
|
}
|
|
if (goLine == 0) {
|
|
name = completeClassNamePHPAtCursor(curs, name, nsName);
|
|
}
|
|
} else {
|
|
QStringList nameList = name.split("::");
|
|
for (int i=0; i<parseResultPHP.classes.size(); i++) {
|
|
ParsePHP::ParseResultClass _cls = parseResultPHP.classes.at(i);
|
|
if (_cls.name == "\\"+nameList.at(0) && _cls.line > 0 && _cls.line-1 != block.blockNumber()) {
|
|
for (int y=0; y<_cls.functionIndexes.size(); y++) {
|
|
if (_cls.functionIndexes.at(y) >= parseResultPHP.functions.size()) break;
|
|
ParsePHP::ParseResultFunction _func = parseResultPHP.functions.at(_cls.functionIndexes.at(y));
|
|
if (_func.name == nameList.at(1) && _func.line > 0 && _func.line-1 != block.blockNumber()) {
|
|
goLine = _func.line;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (cursorText.size() > 0 && mode == MODE_JS) {
|
|
name = cursorText;
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QChar prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
for (int i=0; i<parseResultJS.functions.size(); i++) {
|
|
ParseJS::ParseResultFunction _func = parseResultJS.functions.at(i);
|
|
if (prevChar == "." && _func.clsName.size() == 0) continue;
|
|
if (_func.name == name && _func.line > 0 && _func.line-1 != block.blockNumber()) {
|
|
goLine = _func.line;
|
|
}
|
|
}
|
|
}
|
|
if (goLine > 0) {
|
|
gotoLine(goLine);
|
|
} else if (name.size() > 0) {
|
|
emit showDeclaration(getTabIndex(), name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::showHelpRequested()
|
|
{
|
|
hideCompletePopup();
|
|
|
|
QTextCursor curs = textCursor();
|
|
QTextBlock block = curs.block();
|
|
QString blockText = block.text();
|
|
int total = blockText.size();
|
|
int pos = curs.positionInBlock();
|
|
std::string mode = highlight->findModeAtCursor(&block, pos);
|
|
if (!highlight->isStateOpen(&block, pos)) {
|
|
QChar prevChar = '\0', nextChar = '\0';
|
|
if (pos > 0 && curs.selectedText().size()==0) prevChar = blockText[pos - 1];
|
|
if (pos < total && curs.selectedText().size()==0) nextChar = blockText[pos];
|
|
// text under cursor
|
|
QString cursorText = "", prevText = "", nextText = "";
|
|
bool hasAlpha = false;
|
|
|
|
QChar cursorTextPrevChar = '\0';
|
|
int cursorTextPos = pos;
|
|
if (curs.selectedText().size()==0) {
|
|
for (int i=pos; i>0; i--) {
|
|
QString c = blockText.mid(i-1, 1);
|
|
if (!hasAlpha && isalpha(c[0].toLatin1())) hasAlpha = true;
|
|
cursorTextPrevChar = c[0];
|
|
if (mode == MODE_PHP) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_" || c=="\\") prevText = c + prevText;
|
|
else break;
|
|
} else if (mode == MODE_JS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") prevText = c + prevText;
|
|
else break;
|
|
} else {
|
|
if (isalnum(c[0].toLatin1()) || c=="_") prevText = c + prevText;
|
|
else break;
|
|
}
|
|
cursorTextPos = i-1;
|
|
}
|
|
for (int i=pos; i<total; i++) {
|
|
QString c = blockText.mid(i, 1);
|
|
if (!hasAlpha && isalpha(c[0].toLatin1())) hasAlpha = true;
|
|
if (mode == MODE_PHP) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_" || c=="\\") nextText += c;
|
|
else break;
|
|
} else if (mode == MODE_JS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="$" || c=="_") nextText += c;
|
|
else break;
|
|
} else if (mode == MODE_CSS) {
|
|
if (isalnum(c[0].toLatin1()) || c=="#" || c=="." || c=="-" || c=="_") nextText += c;
|
|
else break;
|
|
} else {
|
|
if (isalnum(c[0].toLatin1()) || c=="_") nextText += c;
|
|
else break;
|
|
}
|
|
}
|
|
if (prevText.size()>0 && nextText.size()>0 && hasAlpha) {
|
|
cursorText = prevText + nextText;
|
|
}
|
|
} else {
|
|
cursorText = curs.selectedText();
|
|
int cp = curs.selectionStart();
|
|
curs.clearSelection();
|
|
curs.setPosition(cp);
|
|
if (cursorText.indexOf("::") >= 0) {
|
|
int p = cursorText.lastIndexOf("::");
|
|
cursorText = cursorText.mid(p+2);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, p+2);
|
|
}
|
|
cursorTextPos = curs.positionInBlock();
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor);
|
|
}
|
|
QString name = "";
|
|
if (cursorText.size() > 0 && mode == MODE_PHP) {
|
|
name = cursorText;
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QChar prevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QChar prevPrevChar = findPrevCharNonSpaceAtCursos(curs);
|
|
QString nsName = highlight->findNsPHPAtCursor(&block, cursorTextPos);
|
|
QString clsName = highlight->findClsPHPAtCursor(&block, cursorTextPos);
|
|
QString funcName = highlight->findFuncPHPAtCursor(&block, cursorTextPos);
|
|
if ((prevChar == ">" && prevPrevChar == "-") || (prevChar == ":" && prevPrevChar == ":")) {
|
|
curs.movePosition(QTextCursor::StartOfBlock);
|
|
curs.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, cursorTextPos);
|
|
QString prevType = detectCompleteTypeAtCursorPHP(curs, nsName, clsName, funcName);
|
|
if (prevType.size() > 0 && prevType.at(0) == "\\") prevType = prevType.mid(1);
|
|
if (prevType.size() > 0) {
|
|
name = prevType + "::" + cursorText;
|
|
}
|
|
} else {
|
|
name = completeClassNamePHPAtCursor(curs, name, nsName);
|
|
}
|
|
}
|
|
if (name.size() > 0) {
|
|
emit showHelp(getTabIndex(), name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::showSearchRequested()
|
|
{
|
|
findToggle();
|
|
}
|
|
|
|
void Editor::onUndoAvailable(bool available)
|
|
{
|
|
isUndoAvailable = available;
|
|
emit undoRedoChanged(getTabIndex());
|
|
}
|
|
|
|
void Editor::onRedoAvailable(bool available)
|
|
{
|
|
isRedoAvailable = available;
|
|
emit undoRedoChanged(getTabIndex());
|
|
}
|
|
|
|
bool Editor::isUndoable()
|
|
{
|
|
return isUndoAvailable;
|
|
}
|
|
|
|
bool Editor::isRedoable()
|
|
{
|
|
return isRedoAvailable;
|
|
}
|
|
|
|
bool Editor::isBackable()
|
|
{
|
|
return backPositions.size() > 1;
|
|
}
|
|
|
|
bool Editor::isForwadable()
|
|
{
|
|
return forwardPositions.size() > 0;
|
|
}
|
|
|
|
void Editor::back()
|
|
{
|
|
if (!isBackable()) return;
|
|
int position = backPositions.last();
|
|
backPositions.removeLast();
|
|
forwardPositions.append(position);
|
|
position = backPositions.last();
|
|
|
|
blockSignals(true);
|
|
QTextCursor cursor = textCursor();
|
|
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
|
if (position >= 0 && position <= cursor.position()) {
|
|
cursor.setPosition(position);
|
|
setTextCursor(cursor);
|
|
setFocus();
|
|
}
|
|
blockSignals(false);
|
|
int line = cursor.blockNumber() + 1;
|
|
scrollToMiddle(cursor, line);
|
|
|
|
emit backForwardChanged(getTabIndex());
|
|
}
|
|
|
|
void Editor::forward()
|
|
{
|
|
if (!isForwadable()) return;
|
|
int position = forwardPositions.last();
|
|
forwardPositions.removeLast();
|
|
backPositions.append(position);
|
|
|
|
blockSignals(true);
|
|
QTextCursor cursor = textCursor();
|
|
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
|
if (position >= 0 && position <= cursor.position()) {
|
|
cursor.setPosition(position);
|
|
setTextCursor(cursor);
|
|
setFocus();
|
|
}
|
|
blockSignals(false);
|
|
int line = cursor.blockNumber() + 1;
|
|
scrollToMiddle(cursor, line);
|
|
|
|
emit backForwardChanged(getTabIndex());
|
|
}
|
|
|
|
void Editor::reloadRequested()
|
|
{
|
|
if (fileName.size() == 0 || !Helper::fileExists(fileName)) return;
|
|
if (!Helper::showQuestion(tr("Reload"), tr("Reload %1 ?").arg(fileName))) return;
|
|
QString txt = Helper::loadFile(fileName, getEncoding(), getFallbackEncoding());
|
|
QString _fileName = fileName;
|
|
QString _extension = extension;
|
|
reset();
|
|
highlight->resetMode();
|
|
setFileName(_fileName);
|
|
convertNewLines(txt);
|
|
setPlainText(txt);
|
|
//resetExtraSelections();
|
|
initMode(_extension);
|
|
setFocus();
|
|
detectTabsMode();
|
|
|
|
emit modifiedStateChanged(tabIndex, modified);
|
|
emit reloaded(tabIndex);
|
|
initHighlighter();
|
|
}
|
|
|
|
void Editor::scrollToMiddle(QTextCursor cursor, int line)
|
|
{
|
|
if (line <= 0) return;
|
|
if (verticalScrollBar()->isVisible()) {
|
|
int minV = verticalScrollBar()->minimum();
|
|
int maxV = verticalScrollBar()->maximum();
|
|
int v = verticalScrollBar()->value();
|
|
int firstVisibleBlockIndex = getFirstVisibleBlockIndex();
|
|
int lastVisibleBlockIndex = getLastVisibleBlockIndex();
|
|
int blockHeight = static_cast<int>(document()->documentLayout()->blockBoundingRect(cursor.block()).height());
|
|
int visibleBlocksCount = lastVisibleBlockIndex - firstVisibleBlockIndex;
|
|
int middleLine = firstVisibleBlockIndex + visibleBlocksCount / 2 + 1;
|
|
if (middleLine > line) {
|
|
int offset = (middleLine - line) * blockHeight;
|
|
if (offset < 0) offset = 0;
|
|
int newV = v - offset;
|
|
if (newV < minV) newV = minV;
|
|
verticalScrollBar()->setValue(newV);
|
|
} else if (middleLine < line) {
|
|
int offset = (line - middleLine) * blockHeight;
|
|
if (offset < 0) offset = 0;
|
|
int newV = v + offset;
|
|
if (newV > maxV) newV = maxV;
|
|
verticalScrollBar()->setValue(newV);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Editor::searchInFilesRequested()
|
|
{
|
|
QTextCursor curs = textCursor();
|
|
QString text = curs.selectedText();
|
|
emit searchInFiles(text);
|
|
}
|
|
|
|
void Editor::qaBtnClicked()
|
|
{
|
|
emit breadcrumbsClick(tabIndex);
|
|
}
|