diff --git a/src/document/katedocument.h b/src/document/katedocument.h index 2495ff97..29aa54e1 100644 --- a/src/document/katedocument.h +++ b/src/document/katedocument.h @@ -1,1414 +1,1412 @@ /* This file is part of the KDE libraries Copyright (C) 2001-2004 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 1999 Jochen Wilhelmy Copyright (C) 2006 Hamish Rodda This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KATE_DOCUMENT_H_ #define _KATE_DOCUMENT_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "katetextline.h" class KateTemplateHandler; namespace KTextEditor { class Plugin; class Attribute; class TemplateScript; } namespace KIO { class TransferJob; } namespace Kate { class SwapFile; } class KateBuffer; namespace KTextEditor { class ViewPrivate; } class KateDocumentConfig; class KateHighlighting; class KateUndoManager; class KateOnTheFlyChecker; class KateDocumentTest; class KateAutoIndent; class KateModOnHdPrompt; /** * @brief Backend of KTextEditor::Document related public KTextEditor interfaces. * * @warning This file is @e private API and not part of the public * KTextEditor interfaces. */ class KTEXTEDITOR_EXPORT KTextEditor::DocumentPrivate : public KTextEditor::Document, public KTextEditor::MarkInterface, public KTextEditor::ModificationInterface, public KTextEditor::ConfigInterface, public KTextEditor::AnnotationInterface, public KTextEditor::MovingInterface, private KTextEditor::MovingRangeFeedback { Q_OBJECT Q_INTERFACES(KTextEditor::MarkInterface) Q_INTERFACES(KTextEditor::ModificationInterface) Q_INTERFACES(KTextEditor::AnnotationInterface) Q_INTERFACES(KTextEditor::ConfigInterface) Q_INTERFACES(KTextEditor::MovingInterface) friend class KTextEditor::Document; friend class ::KateDocumentTest; friend class ::KateBuffer; public: explicit DocumentPrivate(bool bSingleViewMode = false, bool bReadOnly = false, QWidget *parentWidget = nullptr, QObject * = nullptr); ~DocumentPrivate() override; using ReadWritePart::closeUrl; bool closeUrl() override; bool openUrl(const QUrl &url) override; KTextEditor::Range rangeOnLine(KTextEditor::Range range, int line) const; private: void showAndSetOpeningErrorAccess(); /* * Overload this to have on-demand view creation */ public: /** * @return The widget defined by this part, set by setWidget(). */ QWidget *widget() override; public: bool readOnly() const { return m_bReadOnly; } bool singleViewMode() const { return m_bSingleViewMode; } private: // only to make part work, don't change it ! const bool m_bSingleViewMode; const bool m_bReadOnly; // // KTextEditor::Document stuff // public: KTextEditor::View *createView(QWidget *parent, KTextEditor::MainWindow *mainWindow = nullptr) override; QList views() const override { return m_viewsCache; } virtual KTextEditor::View *activeView() const { return m_activeView; } private: QHash m_views; KTextEditor::View *m_activeView = nullptr; // // KTextEditor::EditInterface stuff // public Q_SLOTS: bool setText(const QString &) override; bool setText(const QStringList &text) override; bool clear() override; bool insertText(const KTextEditor::Cursor &position, const QString &s, bool block = false) override; bool insertText(const KTextEditor::Cursor &position, const QStringList &text, bool block = false) override; bool insertLine(int line, const QString &s) override; bool insertLines(int line, const QStringList &s) override; bool removeText(const KTextEditor::Range &range, bool block = false) override; bool removeLine(int line) override; bool replaceText(const KTextEditor::Range &range, const QString &s, bool block = false) override; // unhide method... bool replaceText(const KTextEditor::Range &r, const QStringList &l, bool b) override { return KTextEditor::Document::replaceText(r, l, b); } public: bool isEditingTransactionRunning() const override; QString text(const KTextEditor::Range &range, bool blockwise = false) const override; QStringList textLines(const KTextEditor::Range &range, bool block = false) const override; QString text() const override; QString line(int line) const override; QChar characterAt(const KTextEditor::Cursor &position) const override; QString wordAt(const KTextEditor::Cursor &cursor) const override; KTextEditor::Range wordRangeAt(const KTextEditor::Cursor &cursor) const override; bool isValidTextPosition(const KTextEditor::Cursor& cursor) const override; int lines() const override; bool isLineModified(int line) const override; bool isLineSaved(int line) const override; bool isLineTouched(int line) const override; KTextEditor::Cursor documentEnd() const override; int totalCharacters() const override; int lineLength(int line) const override; Q_SIGNALS: void charactersSemiInteractivelyInserted(const KTextEditor::Cursor &position, const QString &text); /** * The \p document emits this signal whenever text was inserted. The * insertion occurred at range.start(), and new text now occupies up to * range.end(). * \param document document which emitted this signal * \param range range that the newly inserted text occupies * \see insertText(), insertLine() */ void textInserted(KTextEditor::Document *document, const KTextEditor::Range &range); /** * The \p document emits this signal whenever \p range was removed, i.e. * text was removed. * \param document document which emitted this signal * \param range range that the removed text previously occupied * \param oldText the text that has been removed * \see removeText(), removeLine(), clear() */ void textRemoved(KTextEditor::Document *document, const KTextEditor::Range &range, const QString &oldText); public: //BEGIN editStart/editEnd (start, end, undo, cursor update, view update) /** * Enclose editor actions with @p editStart() and @p editEnd() to group * them. */ bool editStart(); /** * Alias for @p editStart() */ void editBegin() { editStart(); } /** * End a editor operation. * @see editStart() */ bool editEnd(); void pushEditState(); void popEditState(); virtual bool startEditing() { return editStart(); } virtual bool finishEditing() { return editEnd(); } //END editStart/editEnd void inputMethodStart(); void inputMethodEnd(); //BEGIN LINE BASED INSERT/REMOVE STUFF (editStart() and editEnd() included) /** * Add a string in the given line/column * @param line line number * @param col column * @param s string to be inserted * @return true on success */ bool editInsertText(int line, int col, const QString &s); /** * Remove a string in the given line/column * @param line line number * @param col column * @param len length of text to be removed * @return true on success */ bool editRemoveText(int line, int col, int len); /** * Mark @p line as @p autowrapped. This is necessary if static word warp is * enabled, because we have to know whether to insert a new line or add the * wrapped words to the followin line. * @param line line number * @param autowrapped autowrapped? * @return true on success */ bool editMarkLineAutoWrapped(int line, bool autowrapped); /** * Wrap @p line. If @p newLine is true, ignore the textline's flag * KateTextLine::flagAutoWrapped and force a new line. Whether a new line * was needed/added you can grab with @p newLineAdded. * @param line line number * @param col column * @param newLine if true, force a new line * @param newLineAdded return value is true, if new line was added (may be 0) * @return true on success */ bool editWrapLine(int line, int col, bool newLine = true, bool *newLineAdded = nullptr); /** * Unwrap @p line. If @p removeLine is true, we force to join the lines. If * @p removeLine is true, @p length is ignored (eg not needed). * @param line line number * @param removeLine if true, force to remove the next line * @return true on success */ bool editUnWrapLine(int line, bool removeLine = true, int length = 0); /** * Insert a string at the given line. * @param line line number * @param s string to insert * @return true on success */ bool editInsertLine(int line, const QString &s); /** * Remove a line * @param line line number * @return true on success */ bool editRemoveLine(int line); bool editRemoveLines(int from, int to); /** * Remove a line * @param startLine line to begin wrapping * @param endLine line to stop wrapping * @return true on success */ bool wrapText(int startLine, int endLine); //END LINE BASED INSERT/REMOVE STUFF Q_SIGNALS: /** * Emmitted when text from @p line was wrapped at position pos onto line @p nextLine. */ void editLineWrapped(int line, int col, int len); /** * Emitted each time text from @p nextLine was upwrapped onto @p line. */ void editLineUnWrapped(int line, int col); public: bool isEditRunning() const; void setUndoMergeAllEdits(bool merge); enum EditingPositionKind { Previous, Next }; /** *Returns the next or previous position cursor in this document from the stack depending on the argument passed. *@return cursor invalid if m_editingStack empty */ KTextEditor::Cursor lastEditingPosition(EditingPositionKind nextOrPrevious, KTextEditor::Cursor); private: int editSessionNumber = 0; QStack editStateStack; bool editIsRunning = false; bool m_undoMergeAllEdits = false; QStack> m_editingStack; int m_editingStackPosition = -1; static const int s_editingStackSizeLimit = 32; // // KTextEditor::UndoInterface stuff // public Q_SLOTS: void undo(); void redo(); /** * Removes all the elements in m_editingStack of the respective document. */ void clearEditingPosStack(); /** * Saves the editing positions into the stack. * If the consecutive editings happens in the same line, then remove * the previous and add the new one with updated column no. */ void saveEditingPositions(KTextEditor::Document *, const KTextEditor::Range &range); public: uint undoCount() const; uint redoCount() const; KateUndoManager *undoManager() { return m_undoManager; } protected: KateUndoManager *const m_undoManager; Q_SIGNALS: void undoChanged(); public: QVector searchText( const KTextEditor::Range &range, const QString &pattern, const KTextEditor::SearchOptions options) const; private: /** * Return a widget suitable to be used as a dialog parent. */ QWidget *dialogParent(); /* * Access to the mode/highlighting subsystem */ public: /** * @copydoc KTextEditor::Document::defaultStyleAt() */ KTextEditor::DefaultStyle defaultStyleAt(const KTextEditor::Cursor &position) const override; /** * Return the name of the currently used mode * \return name of the used mode */ QString mode() const override; /** * Return the name of the currently used mode * \return name of the used mode */ QString highlightingMode() const override; /** * Return a list of the names of all possible modes * \return list of mode names */ QStringList modes() const override; /** * Return a list of the names of all possible modes * \return list of mode names */ QStringList highlightingModes() const override; /** * Set the current mode of the document by giving its name * \param name name of the mode to use for this document * \return \e true on success, otherwise \e false */ bool setMode(const QString &name) override; /** * Set the current mode of the document by giving its name * \param name name of the mode to use for this document * \return \e true on success, otherwise \e false */ bool setHighlightingMode(const QString &name) override; /** * Returns the name of the section for a highlight given its @p index in the highlight * list (as returned by highlightModes()). * You can use this function to build a tree of the highlight names, organized in sections. * \param index in the highlight list for which to find the section name. */ QString highlightingModeSection(int index) const override; /** * Returns the name of the section for a mode given its @p index in the highlight * list (as returned by modes()). * You can use this function to build a tree of the mode names, organized in sections. * \param index index in the highlight list for which to find the section name. */ QString modeSection(int index) const override; /* * Helpers.... */ public: void bufferHlChanged(); /** * allow to mark, that we changed hl on user wish and should not reset it * atm used for the user visible menu to select highlightings */ void setDontChangeHlOnSave(); /** * Set that the BOM marker is forced via the tool menu */ void bomSetByUser(); public: /** * Read session settings from the given \p config. * * Known flags: * "SkipUrl" => don't save/restore the file * "SkipMode" => don't save/restore the mode * "SkipHighlighting" => don't save/restore the highlighting * "SkipEncoding" => don't save/restore the encoding * * \param config read the session settings from this KConfigGroup * \param flags additional flags * \see writeSessionConfig() */ void readSessionConfig(const KConfigGroup &config, const QSet &flags = QSet()) override; /** * Write session settings to the \p config. * See readSessionConfig() for more details. * * \param config write the session settings to this KConfigGroup * \param flags additional flags * \see readSessionConfig() */ void writeSessionConfig(KConfigGroup &config, const QSet &flags = QSet()) override; Q_SIGNALS: void configChanged(); // // KTextEditor::MarkInterface // public Q_SLOTS: void setMark(int line, uint markType) override; void clearMark(int line) override; void addMark(int line, uint markType) override; void removeMark(int line, uint markType) override; void clearMarks() override; void requestMarkTooltip(int line, QPoint position); ///Returns true if the click on the mark should not be further processed bool handleMarkClick(int line); ///Returns true if the context-menu event should not further be processed bool handleMarkContextMenu(int line, QPoint position); void setMarkPixmap(MarkInterface::MarkTypes, const QPixmap &) override; void setMarkDescription(MarkInterface::MarkTypes, const QString &) override; void setEditableMarks(uint markMask) override; public: uint mark(int line) override; const QHash &marks() override; QPixmap markPixmap(MarkInterface::MarkTypes) const override; QString markDescription(MarkInterface::MarkTypes) const override; virtual QColor markColor(MarkInterface::MarkTypes) const; uint editableMarks() const override; Q_SIGNALS: void markToolTipRequested(KTextEditor::Document *document, KTextEditor::Mark mark, QPoint position, bool &handled); void markContextMenuRequested(KTextEditor::Document *document, KTextEditor::Mark mark, QPoint pos, bool &handled); void markClicked(KTextEditor::Document *document, KTextEditor::Mark mark, bool &handled); void marksChanged(KTextEditor::Document *) override; void markChanged(KTextEditor::Document *, KTextEditor::Mark, KTextEditor::MarkInterface::MarkChangeAction) override; private: QHash m_marks; QHash m_markPixmaps; QHash m_markDescriptions; uint m_editableMarks = markType01; // KTextEditor::PrintInterface // public Q_SLOTS: bool print() override; void printPreview() override; // // KTextEditor::DocumentInfoInterface ( ### unfinished ) // public: /** * Tries to detect mime-type based on file name and content of buffer. * * @return the name of the mimetype for the document. */ QString mimeType() override; // // once was KTextEditor::VariableInterface // public: /** * Returns the value for the variable @p name. * If the Document does not have a variable called @p name, * an empty QString() is returned. * * @param name variable to query * @return value of the variable @p name * @see setVariable() */ virtual QString variable(const QString &name) const; /** * Set the variable @p name to @p value. Setting and changing a variable * has immediate effect on the Document. For instance, setting the variable * @e indent-mode to @e cstyle will immediately cause the Document to load * the C Style indenter. * * @param name the variable name * @param value the value to be set * @see variable() */ virtual void setVariable(const QString &name, const QString &value); private: QMap m_storedVariables; // // MovingInterface API // public: /** * Create a new moving cursor for this document. * @param position position of the moving cursor to create * @param insertBehavior insertion behavior * @return new moving cursor for the document */ KTextEditor::MovingCursor *newMovingCursor(const KTextEditor::Cursor &position, KTextEditor::MovingCursor::InsertBehavior insertBehavior = KTextEditor::MovingCursor::MoveOnInsert) override; /** * Create a new moving range for this document. * @param range range of the moving range to create * @param insertBehaviors insertion behaviors * @param emptyBehavior behavior on becoming empty * @return new moving range for the document */ KTextEditor::MovingRange *newMovingRange(const KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors = KTextEditor::MovingRange::DoNotExpand , KTextEditor::MovingRange::EmptyBehavior emptyBehavior = KTextEditor::MovingRange::AllowEmpty) override; /** * Current revision * @return current revision */ qint64 revision() const override; /** * Last revision the buffer got successful saved * @return last revision buffer got saved, -1 if none */ qint64 lastSavedRevision() const override; /** * Lock a revision, this will keep it around until released again. * But all revisions will always be cleared on buffer clear() (and therefor load()) * @param revision revision to lock */ void lockRevision(qint64 revision) override; /** * Release a revision. * @param revision revision to release */ void unlockRevision(qint64 revision) override; /** * Transform a cursor from one revision to an other. * @param cursor cursor to transform * @param insertBehavior behavior of this cursor on insert of text at its position * @param fromRevision from this revision we want to transform * @param toRevision to this revision we want to transform, default of -1 is current revision */ void transformCursor(KTextEditor::Cursor &cursor, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision = -1) override; /** * Transform a cursor from one revision to an other. * @param line line number of the cursor to transform * @param column column number of the cursor to transform * @param insertBehavior behavior of this cursor on insert of text at its position * @param fromRevision from this revision we want to transform * @param toRevision to this revision we want to transform, default of -1 is current revision */ void transformCursor(int &line, int &column, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision = -1) override; /** * Transform a range from one revision to an other. * @param range range to transform * @param insertBehaviors behavior of this range on insert of text at its position * @param emptyBehavior behavior on becoming empty * @param fromRevision from this revision we want to transform * @param toRevision to this revision we want to transform, default of -1 is current revision */ void transformRange(KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision = -1) override; // // MovingInterface Signals // Q_SIGNALS: /** * This signal is emitted before the cursors/ranges/revisions of a document are destroyed as the document is deleted. * @param document the document which the interface belongs too which is in the process of being deleted */ void aboutToDeleteMovingInterfaceContent(KTextEditor::Document *document); /** * This signal is emitted before the ranges of a document are invalidated and the revisions are deleted as the document is cleared (for example on load/reload). * While this signal is emitted, still the old document content is around before the clear. * @param document the document which the interface belongs too which will invalidate its data */ void aboutToInvalidateMovingInterfaceContent(KTextEditor::Document *document); // // Annotation Interface // public: void setAnnotationModel(KTextEditor::AnnotationModel *model) override; KTextEditor::AnnotationModel *annotationModel() const override; Q_SIGNALS: void annotationModelChanged(KTextEditor::AnnotationModel *, KTextEditor::AnnotationModel *); private: KTextEditor::AnnotationModel *m_annotationModel = nullptr; // // KParts::ReadWrite stuff // public: /** * open the file obtained by the kparts framework * the framework abstracts the loading of remote files * @return success */ bool openFile() override; /** * save the file obtained by the kparts framework * the framework abstracts the uploading of remote files * @return success */ bool saveFile() override; void setReadWrite(bool rw = true) override; void setModified(bool m) override; private: void activateDirWatch(const QString &useFileName = QString()); void deactivateDirWatch(); QString m_dirWatchFile; /** * Make backup copy during saveFile, if configured that way. * @return success? else saveFile should return false and not write the file */ bool createBackupFile(); public: /** * Type chars in a view. * Will filter out non-printable chars from the realChars array before inserting. */ bool typeChars(KTextEditor::ViewPrivate *type, const QString &realChars); /** * gets the last line number (lines() - 1) */ inline int lastLine() const { return lines() - 1; } // Repaint all of all of the views void repaintViews(bool paintOnlyDirty = true); KateHighlighting *highlight() const; public Q_SLOTS: void tagLines(int start, int end); private Q_SLOTS: void internalHlChanged(); public: void addView(KTextEditor::View *); /** removes the view from the list of views. The view is *not* deleted. * That's your job. Or, easier, just delete the view in the first place. * It will remove itself. TODO: this could be converted to a private slot * connected to the view's destroyed() signal. It is not currently called * anywhere except from the KTextEditor::ViewPrivate destructor. */ void removeView(KTextEditor::View *); void setActiveView(KTextEditor::View *); bool ownedView(KTextEditor::ViewPrivate *); int toVirtualColumn(int line, int column) const; int toVirtualColumn(const KTextEditor::Cursor &) const; int fromVirtualColumn(int line, int column) const; int fromVirtualColumn(const KTextEditor::Cursor &) const; void newLine(KTextEditor::ViewPrivate *view); // Changes input void backspace(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &); void del(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &); void transpose(const KTextEditor::Cursor &); void paste(KTextEditor::ViewPrivate *view, const QString &text); public: void indent(KTextEditor::Range range, int change); void comment(KTextEditor::ViewPrivate *view, uint line, uint column, int change); void align(KTextEditor::ViewPrivate *view, const KTextEditor::Range &range); void insertTab(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &); enum TextTransform { Uppercase, Lowercase, Capitalize }; /** Handling uppercase, lowercase and capitalize for the view. If there is a selection, that is transformed, otherwise for uppercase or lowercase the character right of the cursor is transformed, for capitalize the word under the cursor is transformed. */ void transform(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &, TextTransform); /** Unwrap a range of lines. */ void joinLines(uint first, uint last); private: bool removeStringFromBeginning(int line, const QString &str); bool removeStringFromEnd(int line, const QString &str); /** Expand tabs to spaces in typed text, if enabled. @param cursorPos The current cursor position for the inserted characters. @param str The typed characters to expand. */ QString eventuallyReplaceTabs(const KTextEditor::Cursor &cursorPos, const QString &str) const; /** Find the position (line and col) of the next char that is not a space. If found line and col point to the found character. Otherwise they have both the value -1. @param line Line of the character which is examined first. @param col Column of the character which is examined first. @return True if the specified or a following character is not a space Otherwise false. */ bool nextNonSpaceCharPos(int &line, int &col); /** Find the position (line and col) of the previous char that is not a space. If found line and col point to the found character. Otherwise they have both the value -1. @return True if the specified or a preceding character is not a space. Otherwise false. */ bool previousNonSpaceCharPos(int &line, int &col); /** * Sets a comment marker as defined by the language providing the attribute * @p attrib on the line @p line */ void addStartLineCommentToSingleLine(int line, int attrib = 0); /** * Removes a comment marker as defined by the language providing the attribute * @p attrib on the line @p line */ bool removeStartLineCommentFromSingleLine(int line, int attrib = 0); /** * @see addStartLineCommentToSingleLine. */ void addStartStopCommentToSingleLine(int line, int attrib = 0); /** *@see removeStartLineCommentFromSingleLine. */ bool removeStartStopCommentFromSingleLine(int line, int attrib = 0); /** *@see removeStartLineCommentFromSingleLine. */ bool removeStartStopCommentFromRegion(const KTextEditor::Cursor &start, const KTextEditor::Cursor &end, int attrib = 0); /** * Add a comment marker as defined by the language providing the attribute * @p attrib to each line in the selection. */ void addStartStopCommentToSelection(KTextEditor::ViewPrivate *view, int attrib = 0); /** * @see addStartStopCommentToSelection. */ void addStartLineCommentToSelection(KTextEditor::ViewPrivate *view, int attrib = 0); /** * Removes comment markers relevant to the language providing * the attribuge @p attrib from each line in the selection. * * @return whether the operation succeeded. */ bool removeStartStopCommentFromSelection(KTextEditor::ViewPrivate *view, int attrib = 0); /** * @see removeStartStopCommentFromSelection. */ bool removeStartLineCommentFromSelection(KTextEditor::ViewPrivate *view, int attrib = 0); public: KTextEditor::Range findMatchingBracket(const KTextEditor::Cursor & start, int maxLines); public: QString documentName() const override { return m_docName; } private: void updateDocName(); public: /** * @return whether the document is modified on disk since last saved */ bool isModifiedOnDisc() { return m_modOnHd; } void setModifiedOnDisk(ModifiedOnDiskReason reason) override; void setModifiedOnDiskWarning(bool on) override; public Q_SLOTS: /** * Ask the user what to do, if the file has been modified on disk. * Reimplemented from KTextEditor::Document. */ virtual void slotModifiedOnDisk(KTextEditor::View *v = nullptr); /** * Reloads the current document from disk if possible */ bool documentReload() override; bool documentSave() override; bool documentSaveAs() override; bool documentSaveAsWithEncoding(const QString &encoding); bool documentSaveCopyAs(); bool save() override; public: bool saveAs(const QUrl &url) override; Q_SIGNALS: /** * Indicate this file is modified on disk * @param doc the KTextEditor::Document object that represents the file on disk * @param isModified indicates the file was modified rather than created or deleted * @param reason the reason we are emitting the signal. */ void modifiedOnDisk(KTextEditor::Document *doc, bool isModified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason) override; private: // helper to handle the embedded notification for externally modified files QPointer m_modOnHdHandler; private Q_SLOTS: void onModOnHdSaveAs(); void onModOnHdReload(); void onModOnHdIgnore(); public: bool setEncoding(const QString &e) override; QString encoding() const override; public Q_SLOTS: void setWordWrap(bool on); void setWordWrapAt(uint col); public: bool wordWrap() const; uint wordWrapAt() const; public Q_SLOTS: void setPageUpDownMovesCursor(bool on); public: bool pageUpDownMovesCursor() const; // code folding public: /** * Same as plainKateTextLine(), except that it is made sure * the line is highlighted. */ Kate::TextLine kateTextLine(int i); //! @copydoc KateBuffer::plainLine() Kate::TextLine plainKateTextLine(int i); Q_SIGNALS: void aboutToRemoveText(const KTextEditor::Range &); private Q_SLOTS: void slotModOnHdDirty(const QString &path); void slotModOnHdCreated(const QString &path); void slotModOnHdDeleted(const QString &path); void slotDelayedHandleModOnHd(); private: /** * Create a git compatible sha1 checksum of the file, if it is a local file. * The result can be accessed through KateBuffer::digest(). * * @return wheather the operation was attempted and succeeded. */ bool createDigest(); /** * create a string for the modonhd warnings, giving the reason. */ QString reasonedMOHString() const; /** * Removes all trailing whitespace in the document. */ void removeTrailingSpaces(); public: /** * Returns a git compatible sha1 checksum of this document on disk. * @return checksum for this document on disk */ QByteArray checksum() const override; void updateFileType(const QString &newType, bool user = false); QString fileType() const { return m_fileType; } /** * Get access to buffer of this document. * Is needed to create cursors and ranges for example. * @return document buffer */ KateBuffer &buffer() { return *m_buffer; } /** * set indentation mode by user * this will remember that a user did set it and will avoid reset on save */ void rememberUserDidSetIndentationMode() { m_indenterSetByUser = true; } /** * User did set encoding for next reload => enforce it! */ void userSetEncodingForNextReload() { m_userSetEncodingForNextReload = true; } // // REALLY internal data ;) // private: // text buffer KateBuffer *const m_buffer; // indenter KateAutoIndent *const m_indenter; bool m_hlSetByUser = false; bool m_bomSetByUser = false; bool m_indenterSetByUser = false; bool m_userSetEncodingForNextReload = false; bool m_modOnHd = false; ModifiedOnDiskReason m_modOnHdReason = OnDiskUnmodified; ModifiedOnDiskReason m_prevModOnHdReason = OnDiskUnmodified; QString m_docName; int m_docNameNumber = 0; // file type !!! QString m_fileType; bool m_fileTypeSetByUser = false; /** * document is still reloading a file */ bool m_reloading = false; public Q_SLOTS: void slotQueryClose_save(bool *handled, bool *abortClosing); public: bool queryClose() override; static bool checkOverwrite(QUrl u, QWidget *parent); /** * Configuration */ public: KateDocumentConfig *config() { return m_config; } KateDocumentConfig *config() const { return m_config; } void updateConfig(); private: void makeAttribs(bool needInvalidate = true); KateDocumentConfig *const m_config; /** * Variable Reader * TODO add register functionality/ktexteditor interface */ private: /** * read dir config file */ void readDirConfig(); /** Reads all the variables in the document. Called when opening/saving a document */ void readVariables(bool onlyViewAndRenderer = false); /** Reads and applies the variables in a single line TODO registered variables gets saved in a [map] */ void readVariableLine(QString t, bool onlyViewAndRenderer = false); /** Sets a view variable in all the views. */ void setViewVariable(QString var, QString val); /** @return weather a string value could be converted to a bool value as supported. The value is put in *result. */ static bool checkBoolValue(QString value, bool *result); /** @return weather a string value could be converted to a integer value. The value is put in *result. */ static bool checkIntValue(QString value, int *result); /** Feeds value into @p col using QColor::setNamedColor() and returns wheather the color is valid */ static bool checkColorValue(QString value, QColor &col); bool m_fileChangedDialogsActivated = false; // // KTextEditor::ConfigInterface // public: QStringList configKeys() const override; QVariant configValue(const QString &key) override; void setConfigValue(const QString &key, const QVariant &value) override; // // KTextEditor::RecoveryInterface // public: bool isDataRecoveryAvailable() const override; void recoverData() override; void discardDataRecovery() override; // // Highlighting information // public: QStringList embeddedHighlightingModes() const override; QString highlightingModeAt(const KTextEditor::Cursor &position) override; // TODO KDE5: move to View virtual KTextEditor::Attribute::Ptr attributeAt(const KTextEditor::Cursor &position); // //BEGIN: KTextEditor::MessageInterface // public: bool postMessage(KTextEditor::Message *message) override; public Q_SLOTS: void messageDestroyed(KTextEditor::Message *message); private: QHash > > m_messageHash; //END KTextEditor::MessageInterface public: QString defaultDictionary() const; QList > dictionaryRanges() const; bool isOnTheFlySpellCheckingEnabled() const; QString dictionaryForMisspelledRange(const KTextEditor::Range &range) const; void clearMisspellingForWord(const QString &word); public Q_SLOTS: void clearDictionaryRanges(); void setDictionary(const QString &dict, const KTextEditor::Range &range); void revertToDefaultDictionary(const KTextEditor::Range &range); void setDefaultDictionary(const QString &dict); void onTheFlySpellCheckingEnabled(bool enable); void refreshOnTheFlyCheck(const KTextEditor::Range &range = KTextEditor::Range::invalid()); Q_SIGNALS: void dictionaryRangesPresent(bool yesNo); void defaultDictionaryChanged(KTextEditor::DocumentPrivate *document); public: bool containsCharacterEncoding(const KTextEditor::Range &range); typedef QList > OffsetList; int computePositionWrtOffsets(const OffsetList &offsetList, int pos); /** * The first OffsetList is from decoded to encoded, and the second OffsetList from * encoded to decoded. **/ QString decodeCharacters(const KTextEditor::Range &range, KTextEditor::DocumentPrivate::OffsetList &decToEncOffsetList, KTextEditor::DocumentPrivate::OffsetList &encToDecOffsetList); void replaceCharactersByEncoding(const KTextEditor::Range &range); - enum EncodedCharaterInsertionPolicy {EncodeAlways, EncodeWhenPresent, EncodeNever}; - protected: KateOnTheFlyChecker *m_onTheFlyChecker = nullptr; QString m_defaultDictionary; QList > m_dictionaryRanges; // from KTextEditor::MovingRangeFeedback void rangeInvalid(KTextEditor::MovingRange *movingRange) override; void rangeEmpty(KTextEditor::MovingRange *movingRange) override; void deleteDictionaryRange(KTextEditor::MovingRange *movingRange); private: Kate::SwapFile *m_swapfile; public: Kate::SwapFile *swapFile(); //helpers for scripting and codefolding int defStyleNum(int line, int column); bool isComment(int line, int column); public: /** * Find the next modified/saved line, starting at @p startLine. If @p down * is \e true, the search is performed downwards, otherwise upwards. * @return the touched line in the requested search direction, or -1 if not found */ int findTouchedLine(int startLine, bool down); private Q_SLOTS: /** * watch for all started io jobs to remember if file is perhaps loading atm * @param job started job */ void slotStarted(KIO::Job *job); void slotCompleted(); void slotCanceled(); /** * trigger display of loading message, after 1000 ms */ void slotTriggerLoadingMessage(); /** * Abort loading */ void slotAbortLoading(); void slotUrlChanged(const QUrl &url); private: /** * different possible states */ enum DocumentStates { /** * Idle */ DocumentIdle, /** * Loading */ DocumentLoading, /** * Saving */ DocumentSaving, /** * Pre Saving As, this is between ::saveAs is called and ::save */ DocumentPreSavingAs, /** * Saving As */ DocumentSavingAs }; /** * current state */ DocumentStates m_documentState = DocumentIdle; /** * read-write state before loading started */ bool m_readWriteStateBeforeLoading = false; /** * if the document is untitled */ bool m_isUntitled = true; /** * loading job, we want to cancel with cancel in the loading message */ QPointer m_loadingJob; /** * message to show during loading */ QPointer m_loadingMessage; /** * Was there any open error on last file loading? */ bool m_openingError = false; /** * Last open file error message */ QString m_openingErrorMessage; public: /** * reads the line length limit from config, if it is not overriden */ int lineLengthLimit() const; public Q_SLOTS: void openWithLineLengthLimitOverride(); private: /** * timer for delayed handling of mod on hd */ QTimer m_modOnHdTimer; private: /** * currently active template handler; there can be only one */ QPointer m_activeTemplateHandler; private: /** * current autobrace range */ QSharedPointer m_currentAutobraceRange; /** * current autobrace closing charater (e.g. ']') */ QChar m_currentAutobraceClosingChar; private Q_SLOTS: void checkCursorForAutobrace(KTextEditor::View* view, const KTextEditor::Cursor& newPos); public: void setActiveTemplateHandler(KateTemplateHandler* handler); Q_SIGNALS: void loaded(KTextEditor::DocumentPrivate *document); private Q_SLOTS: /** * trigger a close of this document in the application */ void closeDocumentInApplication(); private: // To calculate a QHash.keys() is quite expensive, // better keep a copy of that list updated when a view is added or removed. QList m_viewsCache; }; #endif diff --git a/src/spellcheck/spellcheck.cpp b/src/spellcheck/spellcheck.cpp index 9e04d788..308f64e1 100644 --- a/src/spellcheck/spellcheck.cpp +++ b/src/spellcheck/spellcheck.cpp @@ -1,291 +1,286 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2009 by Michel Ludwig * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* If ever threads should be used again, thread communication and * synchronization ought to be done with blocking queued signal connections. */ #include "spellcheck.h" #include #include #include #include #include #include #include "katedocument.h" #include "katehighlight.h" KateSpellCheckManager::KateSpellCheckManager(QObject *parent) : QObject(parent) { } KateSpellCheckManager::~KateSpellCheckManager() { } QStringList KateSpellCheckManager::suggestions(const QString &word, const QString &dictionary) { Sonnet::Speller speller; speller.setLanguage(dictionary); return speller.suggest(word); } void KateSpellCheckManager::ignoreWord(const QString &word, const QString &dictionary) { Sonnet::Speller speller; speller.setLanguage(dictionary); speller.addToSession(word); } void KateSpellCheckManager::addToDictionary(const QString &word, const QString &dictionary) { Sonnet::Speller speller; speller.setLanguage(dictionary); speller.addToPersonal(word); } QList KateSpellCheckManager::rangeDifference(const KTextEditor::Range &r1, const KTextEditor::Range &r2) { Q_ASSERT(r1.contains(r2)); QList toReturn; KTextEditor::Range before(r1.start(), r2.start()); KTextEditor::Range after(r2.end(), r1.end()); if (!before.isEmpty()) { toReturn.push_back(before); } if (!after.isEmpty()) { toReturn.push_back(after); } return toReturn; } namespace { bool lessThanRangeDictionaryPair(const QPair &s1, const QPair &s2) { return s1.first.end() <= s2.first.start(); } } QList > KateSpellCheckManager::spellCheckLanguageRanges(KTextEditor::DocumentPrivate *doc, const KTextEditor::Range &range) { QString defaultDict = doc->defaultDictionary(); QList toReturn; QList > dictionaryRanges = doc->dictionaryRanges(); if (dictionaryRanges.isEmpty()) { toReturn.push_back(RangeDictionaryPair(range, defaultDict)); return toReturn; } QList splitQueue; splitQueue.push_back(range); while (!splitQueue.isEmpty()) { bool handled = false; KTextEditor::Range consideredRange = splitQueue.takeFirst(); for (QList >::iterator i = dictionaryRanges.begin(); i != dictionaryRanges.end(); ++i) { KTextEditor::Range languageRange = *((*i).first); KTextEditor::Range intersection = languageRange.intersect(consideredRange); if (intersection.isEmpty()) { continue; } toReturn.push_back(RangeDictionaryPair(intersection, (*i).second)); splitQueue += rangeDifference(consideredRange, intersection); handled = true; break; } if (!handled) { // 'consideredRange' did not intersect with any dictionary range, so we add it with the default dictionary toReturn.push_back(RangeDictionaryPair(consideredRange, defaultDict)); } } // finally, we still have to sort the list qStableSort(toReturn.begin(), toReturn.end(), lessThanRangeDictionaryPair); return toReturn; } QList > KateSpellCheckManager::spellCheckWrtHighlightingRanges(KTextEditor::DocumentPrivate *document, const KTextEditor::Range &range, const QString &dictionary, bool singleLine, bool returnSingleRange) { QList > toReturn; if (range.isEmpty()) { return toReturn; } KateHighlighting *highlighting = document->highlight(); QList rangesToSplit; if (!singleLine || range.onSingleLine()) { rangesToSplit.push_back(range); } else { const int startLine = range.start().line(); const int startColumn = range.start().column(); const int endLine = range.end().line(); const int endColumn = range.end().column(); for (int line = startLine; line <= endLine; ++line) { const int start = (line == startLine) ? startColumn : 0; const int end = (line == endLine) ? endColumn : document->lineLength(line); KTextEditor::Range toAdd(line, start, line, end); if (!toAdd.isEmpty()) { rangesToSplit.push_back(toAdd); } } } for (QList::iterator i = rangesToSplit.begin(); i != rangesToSplit.end(); ++i) { KTextEditor::Range rangeToSplit = *i; KTextEditor::Cursor begin = KTextEditor::Cursor::invalid(); const int startLine = rangeToSplit.start().line(); const int startColumn = rangeToSplit.start().column(); const int endLine = rangeToSplit.end().line(); const int endColumn = rangeToSplit.end().column(); bool inSpellCheckArea = false; for (int line = startLine; line <= endLine; ++line) { Kate::TextLine kateTextLine = document->kateTextLine(line); if (!kateTextLine) { continue; // bug #303496 } const int start = (line == startLine) ? startColumn : 0; const int end = (line == endLine) ? endColumn : kateTextLine->length(); for (int i = start; i < end;) { // WARNING: 'i' has to be incremented manually! int attr = kateTextLine->attribute(i); const KatePrefixStore &prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr); QString prefixFound = prefixStore.findPrefix(kateTextLine, i); if (!document->highlight()->attributeRequiresSpellchecking(static_cast(attr)) && prefixFound.isEmpty()) { if (i == start) { ++i; continue; } else if (inSpellCheckArea) { KTextEditor::Range spellCheckRange(begin, KTextEditor::Cursor(line, i)); // work around Qt bug 6498 trimRange(document, spellCheckRange); if (!spellCheckRange.isEmpty()) { toReturn.push_back(RangeDictionaryPair(spellCheckRange, dictionary)); if (returnSingleRange) { return toReturn; } } begin = KTextEditor::Cursor::invalid(); inSpellCheckArea = false; } } else if (!inSpellCheckArea) { begin = KTextEditor::Cursor(line, i); inSpellCheckArea = true; } if (!prefixFound.isEmpty()) { i += prefixFound.length(); } else { ++i; } } } if (inSpellCheckArea) { KTextEditor::Range spellCheckRange(begin, rangeToSplit.end()); // work around Qt bug 6498 trimRange(document, spellCheckRange); if (!spellCheckRange.isEmpty()) { toReturn.push_back(RangeDictionaryPair(spellCheckRange, dictionary)); if (returnSingleRange) { return toReturn; } } } } return toReturn; } QList > KateSpellCheckManager::spellCheckRanges(KTextEditor::DocumentPrivate *doc, const KTextEditor::Range &range, bool singleLine) { QList toReturn; QList languageRangeList = spellCheckLanguageRanges(doc, range); for (QList::iterator i = languageRangeList.begin(); i != languageRangeList.end(); ++i) { const RangeDictionaryPair &p = *i; toReturn += spellCheckWrtHighlightingRanges(doc, p.first, p.second, singleLine); } return toReturn; } void KateSpellCheckManager::replaceCharactersEncodedIfNecessary(const QString &newWord, KTextEditor::DocumentPrivate *doc, const KTextEditor::Range &replacementRange) { - const QString replacedString = doc->text(replacementRange); - int attr = doc->kateTextLine(replacementRange.start().line())->attribute(replacementRange.start().column()); - int p = doc->highlight()->getEncodedCharactersInsertionPolicy(attr); - - if ((p == KTextEditor::DocumentPrivate::EncodeAlways) - || (p == KTextEditor::DocumentPrivate::EncodeWhenPresent && doc->containsCharacterEncoding(replacementRange))) { + if (doc->containsCharacterEncoding(replacementRange)) { doc->replaceText(replacementRange, newWord); doc->replaceCharactersByEncoding(KTextEditor::Range(replacementRange.start(), replacementRange.start() + KTextEditor::Cursor(0, newWord.length()))); } else { doc->replaceText(replacementRange, newWord); } } void KateSpellCheckManager::trimRange(KTextEditor::DocumentPrivate *doc, KTextEditor::Range &r) { if (r.isEmpty()) { return; } KTextEditor::Cursor cursor = r.start(); while (cursor < r.end()) { if (doc->lineLength(cursor.line()) > 0 && !doc->characterAt(cursor).isSpace() && doc->characterAt(cursor).category() != QChar::Other_Control) { break; } cursor.setColumn(cursor.column() + 1); if (cursor.column() >= doc->lineLength(cursor.line())) { cursor.setPosition(cursor.line() + 1, 0); } } r.setStart(cursor); if (r.isEmpty()) { return; } cursor = r.end(); KTextEditor::Cursor prevCursor = cursor; // the range cannot be empty now do { prevCursor = cursor; if (cursor.column() <= 0) { cursor.setPosition(cursor.line() - 1, doc->lineLength(cursor.line() - 1)); } else { cursor.setColumn(cursor.column() - 1); } if (cursor.column() < doc->lineLength(cursor.line()) && !doc->characterAt(cursor).isSpace() && doc->characterAt(cursor).category() != QChar::Other_Control) { break; } } while (cursor > r.start()); r.setEnd(prevCursor); } diff --git a/src/syntax/katehighlight.cpp b/src/syntax/katehighlight.cpp index 29f52b92..1e7e7ad0 100644 --- a/src/syntax/katehighlight.cpp +++ b/src/syntax/katehighlight.cpp @@ -1,680 +1,675 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Mirko Stocker Copyright (C) 2007 Matthew Woehlke Copyright (C) 2003, 2004 Anders Lund Copyright (C) 2003 Hamish Rodda Copyright (C) 2001,2002 Joseph Wenninger Copyright (C) 2001 Christoph Cullmann Copyright (C) 1999 Jochen Wilhelmy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //BEGIN INCLUDES #include "katehighlight.h" #include "katetextline.h" #include "katedocument.h" #include "katerenderer.h" #include "kateglobal.h" #include "kateschema.h" #include "kateconfig.h" #include "kateextendedattribute.h" #include "katepartdebug.h" #include "katedefaultcolors.h" #include #include #include #include #include #include #include #include #include //END //BEGIN STATICS namespace { /** * convert from KSyntaxHighlighting => KTextEditor type * special handle non-1:1 things */ inline KTextEditor::DefaultStyle textStyleToDefaultStyle(const KSyntaxHighlighting::Theme::TextStyle textStyle) { // handle deviations if (textStyle == KSyntaxHighlighting::Theme::Error) { return KTextEditor::dsError; } if (textStyle == KSyntaxHighlighting::Theme::Others) { return KTextEditor::dsOthers; } // else: simple cast return static_cast(textStyle); } } //END //BEGIN KateHighlighting KateHighlighting::KateHighlighting(const KSyntaxHighlighting::Definition &def) { /** * get name and section, always works */ iName = def.name(); iSection = def.translatedSection(); /** * handle the "no highlighting" case */ if (!def.isValid()) { // dummy hl info m_additionalData[iName]; m_hlIndex[0] = iName; // be done, all below is just for the real highlighting variants return; } /** * handle the real highlighting case */ noHl = false; iHidden = def.isHidden(); identifier = def.filePath(); iStyle = def.style(); m_indentation = def.indenter(); // FIXME folding = true; m_foldingIndentationSensitive = def.indentationBasedFoldingEnabled(); /** * tell the AbstractHighlighter the definition it shall use */ setDefinition(def); /** * get all included definitions, e.g. PHP for HTML highlighting */ auto definitions = definition().includedDefinitions(); /** * first: handle only really included definitions */ for (const auto &includedDefinition : definitions) embeddedHighlightingModes.push_back(includedDefinition.name()); /** * now: handle all, including this definition itself * create the format => attributes mapping * collect embedded highlightings, too * * we start with our definition as we want to have the default format * of the initial definition as attribute with index == 0 */ definitions.push_front(definition()); for (const auto & includedDefinition : definitions) { // FIXME: right values auto &properties = m_additionalData[includedDefinition.name()]; properties.definition = includedDefinition; for (const auto &emptyLine : includedDefinition.foldingIgnoreList()) properties.emptyLines.push_back(QRegularExpression(emptyLine)); properties.singleLineCommentMarker = includedDefinition.singleLineCommentMarker(); properties.singleLineCommentPosition = includedDefinition.singleLineCommentPosition(); const auto multiLineComment = includedDefinition.multiLineCommentMarker(); properties.multiLineCommentStart = multiLineComment.first; properties.multiLineCommentEnd = multiLineComment.second; // collect formats for (const auto & format : includedDefinition.formats()) { // register format id => internal attributes, we want no clashs const auto nextId = m_formats.size(); m_formatsIdToIndex.insert(std::make_pair(format.id(), nextId)).second; m_formats.push_back(format); m_hlIndex[nextId] = includedDefinition.name(); } } } KateHighlighting::~KateHighlighting() { } void KateHighlighting::doHighlight(const Kate::TextLineData *prevLine, Kate::TextLineData *textLine, const Kate::TextLineData *nextLine, bool &ctxChanged, int tabWidth) { // default: no context change ctxChanged = false; // no text line => nothing to do if (!textLine) { return; } // in all cases, remove old hl, or we will grow to infinite ;) textLine->clearAttributesAndFoldings(); // reset folding start textLine->clearMarkedAsFoldingStart(); // no hl set, nothing to do more than the above cleaning ;) if (noHl) { return; } /** * ensure we arrive in clean state */ Q_ASSERT(!m_textLineToHighlight); Q_ASSERT(!m_foldingStartToCount); /** * highlight the given line via the abstract highlighter * a bit ugly: we set the line to highlight as member to be able to update its stats in the applyFormat and applyFolding member functions */ m_textLineToHighlight = textLine; const KSyntaxHighlighting::State initialState (!prevLine ? KSyntaxHighlighting::State() : prevLine->highlightingState()); const KSyntaxHighlighting::State endOfLineState = highlightLine(textLine->string(), initialState); m_textLineToHighlight = nullptr; /** * update highlighting state if needed */ if (textLine->highlightingState() != endOfLineState) { textLine->setHighlightingState(endOfLineState); ctxChanged = true; } /** * handle folding info computed and cleanup hash again, if there * check if folding is not balanced and we have more starts then ends * then this line is a possible folding start! */ if (m_foldingStartToCount) { /** * possible folding start, if imbalanced, aka hash not empty! */ if (!m_foldingStartToCount->isEmpty()) { textLine->markAsFoldingStartAttribute(); } /** * kill hash */ delete m_foldingStartToCount; m_foldingStartToCount = nullptr; } /** * check for indentation based folding */ if (m_foldingIndentationSensitive && (tabWidth > 0) && !textLine->markedAsFoldingStartAttribute()) { /** * compute if we increase indentation in next line */ if (endOfLineState.indentationBasedFoldingEnabled() && !isEmptyLine(textLine) && !isEmptyLine(nextLine) && (textLine->indentDepth(tabWidth) < nextLine->indentDepth(tabWidth))) { textLine->markAsFoldingStartIndentation(); } } } void KateHighlighting::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) { // WE ATM assume ascending offset order Q_ASSERT(m_textLineToHighlight); Q_ASSERT(format.isValid()); // get internal attribute, must exist const auto it = m_formatsIdToIndex.find(format.id()); Q_ASSERT(it != m_formatsIdToIndex.end()); // remember highlighting info in our textline m_textLineToHighlight->addAttribute(Kate::TextLineData::Attribute(offset, length, it->second)); } void KateHighlighting::applyFolding(int offset, int, KSyntaxHighlighting::FoldingRegion region) { // WE ATM assume ascending offset order and don't care for length Q_ASSERT(m_textLineToHighlight); Q_ASSERT(region.isValid()); const int foldingValue = (region.type() == KSyntaxHighlighting::FoldingRegion::Begin) ? int(region.id()) : -int(region.id()); m_textLineToHighlight->addFolding(offset, foldingValue); /** * for each end region, decrement counter for that type, erase if count reaches 0! */ if ((foldingValue < 0) && m_foldingStartToCount) { QHash::iterator end = m_foldingStartToCount->find(foldingValue); if (end != m_foldingStartToCount->end()) { if (end.value() > 1) { --(end.value()); } else { m_foldingStartToCount->erase(end); } } } /** * increment counter for each begin region! */ if (foldingValue > 0) { // construct on demand! if (!m_foldingStartToCount) { m_foldingStartToCount = new QHash (); } ++(*m_foldingStartToCount)[foldingValue]; } } void KateHighlighting::getKateExtendedAttributeList(const QString &schema, QList &list, KConfig *cfg) { KConfigGroup config(cfg ? cfg : KateHlManager::self()->getKConfig(), QLatin1String("Highlighting ") + iName + QLatin1String(" - Schema ") + schema); list = attributesForDefinition(); foreach (KTextEditor::Attribute::Ptr p, list) { Q_ASSERT(p); QStringList s = config.readEntry(p->name(), QStringList()); // qCDebug(LOG_KTE)<name< 0) { while (s.count() < 10) { s << QString(); } QString name = p->name(); bool spellCheck = !p->skipSpellChecking(); p->clear(); p->setName(name); p->setSkipSpellChecking(!spellCheck); QString tmp = s[0]; if (!tmp.isEmpty()) { p->setDefaultStyle(static_cast (tmp.toInt())); } QRgb col; tmp = s[1]; if (!tmp.isEmpty()) { col = tmp.toUInt(nullptr, 16); p->setForeground(QColor(col)); } tmp = s[2]; if (!tmp.isEmpty()) { col = tmp.toUInt(nullptr, 16); p->setSelectedForeground(QColor(col)); } tmp = s[3]; if (!tmp.isEmpty()) { p->setFontBold(tmp != QLatin1String("0")); } tmp = s[4]; if (!tmp.isEmpty()) { p->setFontItalic(tmp != QLatin1String("0")); } tmp = s[5]; if (!tmp.isEmpty()) { p->setFontStrikeOut(tmp != QLatin1String("0")); } tmp = s[6]; if (!tmp.isEmpty()) { p->setFontUnderline(tmp != QLatin1String("0")); } tmp = s[7]; if (!tmp.isEmpty()) { col = tmp.toUInt(nullptr, 16); p->setBackground(QColor(col)); } tmp = s[8]; if (!tmp.isEmpty()) { col = tmp.toUInt(nullptr, 16); p->setSelectedBackground(QColor(col)); } tmp = s[9]; if (!tmp.isEmpty() && tmp != QLatin1String("---")) { p->setFontFamily(tmp); } } } } void KateHighlighting::getKateExtendedAttributeListCopy(const QString &schema, QList< KTextEditor::Attribute::Ptr > &list, KConfig *cfg) { QList attributes; getKateExtendedAttributeList(schema, attributes, cfg); list.clear(); foreach (const KTextEditor::Attribute::Ptr &attribute, attributes) { list.append(KTextEditor::Attribute::Ptr(new KTextEditor::Attribute(*attribute.data()))); } } void KateHighlighting::setKateExtendedAttributeList(const QString &schema, QList &list, KConfig *cfg, bool writeDefaultsToo) { KConfigGroup config(cfg ? cfg : KateHlManager::self()->getKConfig(), QLatin1String("Highlighting ") + iName + QLatin1String(" - Schema ") + schema); QStringList settings; KateAttributeList defList; KateHlManager::self()->getDefaults(schema, defList); foreach (const KTextEditor::Attribute::Ptr &p, list) { Q_ASSERT(p); settings.clear(); KTextEditor::DefaultStyle defStyle = p->defaultStyle(); KTextEditor::Attribute::Ptr a(defList[defStyle]); settings << QString::number(p->defaultStyle(), 10); settings << (p->hasProperty(QTextFormat::ForegroundBrush) ? QString::number(p->foreground().color().rgb(), 16) : (writeDefaultsToo ? QString::number(a->foreground().color().rgb(), 16) : QString())); settings << (p->hasProperty(SelectedForeground) ? QString::number(p->selectedForeground().color().rgb(), 16) : (writeDefaultsToo ? QString::number(a->selectedForeground().color().rgb(), 16) : QString())); settings << (p->hasProperty(QTextFormat::FontWeight) ? (p->fontBold() ? QStringLiteral("1") : QStringLiteral("0")) : (writeDefaultsToo ? (a->fontBold() ? QStringLiteral("1") : QStringLiteral("0")) : QString())); settings << (p->hasProperty(QTextFormat::FontItalic) ? (p->fontItalic() ? QStringLiteral("1") : QStringLiteral("0")) : (writeDefaultsToo ? (a->fontItalic() ? QStringLiteral("1") : QStringLiteral("0")) : QString())); settings << (p->hasProperty(QTextFormat::FontStrikeOut) ? (p->fontStrikeOut() ? QStringLiteral("1") : QStringLiteral("0")) : (writeDefaultsToo ? (a->fontStrikeOut() ? QStringLiteral("1") : QStringLiteral("0")) : QString())); settings << (p->hasProperty(QTextFormat::FontUnderline) ? (p->fontUnderline() ? QStringLiteral("1") : QStringLiteral("0")) : (writeDefaultsToo ? (a->fontUnderline() ? QStringLiteral("1") : QStringLiteral("0")) : QString())); settings << (p->hasProperty(QTextFormat::BackgroundBrush) ? QString::number(p->background().color().rgb(), 16) : ((writeDefaultsToo && a->hasProperty(QTextFormat::BackgroundBrush)) ? QString::number(a->background().color().rgb(), 16) : QString())); settings << (p->hasProperty(SelectedBackground) ? QString::number(p->selectedBackground().color().rgb(), 16) : ((writeDefaultsToo && a->hasProperty(SelectedBackground)) ? QString::number(a->selectedBackground().color().rgb(), 16) : QString())); settings << (p->hasProperty(QTextFormat::FontFamily) ? (p->fontFamily()) : (writeDefaultsToo ? a->fontFamily() : QString())); settings << QStringLiteral("---"); config.writeEntry(p->name(), settings); } } const QHash &KateHighlighting::getCharacterEncodings(int attrib) const { return additionalData(hlKeyForAttrib(attrib)).characterEncodings; } const KatePrefixStore &KateHighlighting::getCharacterEncodingsPrefixStore(int attrib) const { return additionalData(hlKeyForAttrib(attrib)).characterEncodingsPrefixStore; } const QHash &KateHighlighting::getReverseCharacterEncodings(int attrib) const { return additionalData(hlKeyForAttrib(attrib)).reverseCharacterEncodings; } -int KateHighlighting::getEncodedCharactersInsertionPolicy(int attrib) const -{ - return additionalData(hlKeyForAttrib(attrib)).encodedCharactersInsertionPolicy; -} - void KateHighlighting::addCharacterEncoding(const QString &key, const QString &encoding, const QChar &c) { m_additionalData[ key ].characterEncodingsPrefixStore.addPrefix(encoding); m_additionalData[ key ].characterEncodings[ encoding ] = c; m_additionalData[ key ].reverseCharacterEncodings[ c ] = encoding; } bool KateHighlighting::attributeRequiresSpellchecking(int attr) { if (attr >= 0 && attr < m_formats.size()) { return m_formats[attr].spellCheck(); } return true; } KTextEditor::DefaultStyle KateHighlighting::defaultStyleForAttribute(int attr) const { if (attr >= 0 && attr < m_formats.size()) { return textStyleToDefaultStyle(m_formats[attr].textStyle()); } return KTextEditor::dsNormal; } QString KateHighlighting::hlKeyForAttrib(int i) const { Q_ASSERT(i >= 0 && i < m_hlIndex.size()); return m_hlIndex[i]; } QString KateHighlighting::nameForAttrib(int attrib) const { Q_ASSERT(attrib >= 0 && attrib < m_formats.size()); return hlKeyForAttrib(attrib) + QLatin1Char(':') + m_formats[attrib].name(); } bool KateHighlighting::isInWord(QChar c, int attrib) const { return !additionalData(hlKeyForAttrib(attrib)).definition.isWordDelimiter(c) && !c.isSpace() && c != QLatin1Char('"') && c != QLatin1Char('\'') && c != QLatin1Char('`'); } bool KateHighlighting::canBreakAt(QChar c, int attrib) const { return additionalData(hlKeyForAttrib(attrib)).definition.isWordWrapDelimiter(c) && c != QLatin1Char('"') && c != QLatin1Char('\''); } const QVector &KateHighlighting::emptyLines(int attrib) const { return additionalData(hlKeyForAttrib(attrib)).emptyLines; } bool KateHighlighting::canComment(int startAttrib, int endAttrib) const { QString k = hlKeyForAttrib(startAttrib); return (k == hlKeyForAttrib(endAttrib) && ((!additionalData(k).multiLineCommentStart.isEmpty() && !additionalData(k).multiLineCommentEnd.isEmpty()) || !additionalData(k).singleLineCommentMarker.isEmpty())); } QString KateHighlighting::getCommentStart(int attrib) const { return additionalData(hlKeyForAttrib(attrib)).multiLineCommentStart; } QString KateHighlighting::getCommentEnd(int attrib) const { return additionalData(hlKeyForAttrib(attrib)).multiLineCommentEnd; } QString KateHighlighting::getCommentSingleLineStart(int attrib) const { return additionalData(hlKeyForAttrib(attrib)).singleLineCommentMarker; } KSyntaxHighlighting::CommentPosition KateHighlighting::getCommentSingleLinePosition(int attrib) const { return additionalData(hlKeyForAttrib(attrib)).singleLineCommentPosition; } const QHash &KateHighlighting::characterEncodings(int attrib) const { return additionalData(hlKeyForAttrib(attrib)).characterEncodings; } void KateHighlighting::clearAttributeArrays() { // just clear the hashed attributes, we create them lazy again m_attributeArrays.clear(); } QList KateHighlighting::attributesForDefinition() { /** * create list of all known things */ QList array; if (m_formats.empty()) { KTextEditor::Attribute::Ptr newAttribute(new KTextEditor::Attribute(iName + QLatin1Char(':') + QLatin1String("Normal"), KTextEditor::dsNormal)); array.append(newAttribute); } else { for (const auto &format : m_formats) { /** * FIXME: atm we just set some theme here for later color generation */ setTheme(KateHlManager::self()->repository().defaultTheme(KSyntaxHighlighting::Repository::LightTheme)); /** * create a KTextEditor attribute matching the given format */ KTextEditor::Attribute::Ptr newAttribute(new KTextEditor::Attribute(nameForAttrib(array.size()), textStyleToDefaultStyle(format.textStyle()))); if (format.hasTextColor(theme())) { newAttribute->setForeground(format.textColor(theme())); newAttribute->setSelectedForeground(format.selectedTextColor(theme())); } if (format.hasBackgroundColor(theme())) { newAttribute->setBackground(format.backgroundColor(theme())); newAttribute->setSelectedBackground(format.selectedBackgroundColor(theme())); } if (format.isBold(theme())) { newAttribute->setFontBold(true); } if (format.isItalic(theme())) { newAttribute->setFontItalic(true); } if (format.isUnderline(theme())) { newAttribute->setFontUnderline(true); } if (format.isStrikeThrough(theme())) { newAttribute->setFontStrikeOut(true); } newAttribute->setSkipSpellChecking(format.spellCheck()); array.append(newAttribute); } } return array; } QList KateHighlighting::attributes(const QString &schema) { // found it, already floating around if (m_attributeArrays.contains(schema)) { return m_attributeArrays[schema]; } // k, schema correct, let create the data QList array; KateAttributeList defaultStyleList; KateHlManager::self()->getDefaults(schema, defaultStyleList); QList itemDataList; getKateExtendedAttributeList(schema, itemDataList); uint nAttribs = itemDataList.count(); for (uint z = 0; z < nAttribs; z++) { KTextEditor::Attribute::Ptr itemData = itemDataList.at(z); KTextEditor::Attribute::Ptr newAttribute(new KTextEditor::Attribute(*defaultStyleList.at(itemData->defaultStyle()))); if (itemData && itemData->hasAnyProperty()) { *newAttribute += *itemData; } array.append(newAttribute); } m_attributeArrays.insert(schema, array); return array; } QStringList KateHighlighting::getEmbeddedHighlightingModes() const { return embeddedHighlightingModes; } bool KateHighlighting::isEmptyLine(const Kate::TextLineData *textline) const { const QString &txt = textline->string(); if (txt.isEmpty()) { return true; } const auto &l = emptyLines(textline->attribute(0)); if (l.isEmpty()) { return false; } foreach (const QRegularExpression &re, l) { const QRegularExpressionMatch match = re.match (txt, 0, QRegularExpression::NormalMatch, QRegularExpression::AnchoredMatchOption); if (match.hasMatch() && match.capturedLength() == txt.length()) { return true; } } return false; } int KateHighlighting::attributeForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor) { // Validate parameters to prevent out of range access if (cursor.line() < 0 || cursor.line() >= doc->lines() || cursor.column() < 0) { return 0; } // get highlighted line Kate::TextLine tl = doc->kateTextLine(cursor.line()); // make sure the textline is a valid pointer if (!tl) { return 0; } /** * either get char attribute or attribute of context still active at end of line */ if (cursor.column() < tl->length()) { return tl->attribute(cursor.column()); } else if (cursor.column() >= tl->length()) { if (!tl->attributesList().isEmpty()) { return tl->attributesList().back().attributeValue; } } return 0; } QStringList KateHighlighting::keywordsForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor) { // FIXME-SYNTAX: was before more precise, aka context level const auto &def = additionalData(m_hlIndex[attributeForLocation(doc, cursor)]).definition; QStringList keywords; for (const auto &keylist : def.keywordLists()) { keywords += def.keywordList(keylist); } return keywords; } bool KateHighlighting::spellCheckingRequiredForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor) { return m_formats[attributeForLocation(doc, cursor)].spellCheck(); } QString KateHighlighting::higlightingModeForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor) { return m_hlIndex[attributeForLocation(doc, cursor)]; } //END diff --git a/src/syntax/katehighlight.h b/src/syntax/katehighlight.h index c7475888..aadc2921 100644 --- a/src/syntax/katehighlight.h +++ b/src/syntax/katehighlight.h @@ -1,388 +1,386 @@ /* This file is part of the KDE libraries Copyright (C) 2001,2002 Joseph Wenninger Copyright (C) 2001 Christoph Cullmann Copyright (C) 1999 Jochen Wilhelmy * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __KATE_HIGHLIGHT_H__ #define __KATE_HIGHLIGHT_H__ #include #include #include #include #include "katetextline.h" #include "kateextendedattribute.h" #include "katesyntaxmanager.h" #include "spellcheck/prefixstore.h" #include "range.h" #include #include #include #include #include #include #include #include #include #include class KConfig; namespace KTextEditor { class DocumentPrivate; } class KateHighlighting : private KSyntaxHighlighting::AbstractHighlighter { public: KateHighlighting(const KSyntaxHighlighting::Definition &def); ~KateHighlighting(); protected: /** * Reimplement this to apply formats to your output. The provided @p format * is valid for the interval [@p offset, @p offset + @p length). * * @param offset The start column of the interval for which @p format matches * @param length The length of the matching text * @param format The Format that applies to the range [offset, offset + length) * * @note Make sure to set a valid Definition, otherwise the parameter * @p format is invalid for the entire line passed to highlightLine() * (cf. Format::isValid()). * * @see applyFolding(), highlightLine() */ virtual void applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) override; /** * Reimplement this to apply folding to your output. The provided * FoldingRegion @p region either stars or ends a code folding region in the * interval [@p offset, @p offset + @p length). * * @param offset The start column of the FoldingRegion * @param length The length of the matching text that starts / ends a * folding region * @param region The FoldingRegion that applies to the range [offset, offset + length) * * @note The FoldingRegion @p region is @e always either of type * FoldingRegion::Type::Begin or FoldingRegion::Type::End. * * @see applyFormat(), highlightLine(), FoldingRegion */ virtual void applyFolding(int offset, int length, KSyntaxHighlighting::FoldingRegion region) override; public: /** * Parse the text and fill in the context array and folding list array * * @param prevLine The previous line, the context array is picked up from that if present. * @param textLine The text line to parse * @param nextLine The next line, to check if indentation changed for indentation based folding. * @param ctxChanged will be set to reflect if the context changed * @param tabWidth tab width for indentation based folding, if wanted, else 0 */ void doHighlight(const Kate::TextLineData *prevLine, Kate::TextLineData *textLine, const Kate::TextLineData *nextLine, bool &ctxChanged, int tabWidth = 0); /** * Saves the attribute definitions to the config file. * * @param schema The id of the schema group to save * @param list QList containing the data to be used */ void setKateExtendedAttributeList(const QString &schema, QList &list, KConfig *cfg = nullptr /*if 0 standard kate config*/, bool writeDefaultsToo = false); const QString &name() const { return iName; } const QString §ion() const { return iSection; } bool hidden() const { return iHidden; } const QString &style() const { return iStyle; } const QString &getIdentifier() const { return identifier; } /** * @return true if the character @p c is not a deliminator character * for the corresponding highlight. */ bool isInWord(QChar c, int attrib = 0) const; /** * @return true if the character @p c is a wordwrap deliminator as specified * in the general keyword section of the xml file. */ bool canBreakAt(QChar c, int attrib = 0) const; /** * */ const QVector &emptyLines(int attribute = 0) const; bool isEmptyLine(const Kate::TextLineData *textline) const; /** * @return true if @p beginAttr and @p endAttr are members of the same * highlight, and there are comment markers of either type in that. */ bool canComment(int startAttr, int endAttr) const; /** * @return the mulitiline comment start marker for the highlight * corresponding to @p attrib. */ QString getCommentStart(int attrib = 0) const; /** * @return the muiltiline comment end marker for the highlight corresponding * to @p attrib. */ QString getCommentEnd(int attrib = 0) const; /** * @return the single comment marker for the highlight corresponding * to @p attrib. */ QString getCommentSingleLineStart(int attrib = 0) const; const QHash &characterEncodings(int attrib = 0) const; /** * @return the single comment marker position for the highlight corresponding * to @p attrib. */ KSyntaxHighlighting::CommentPosition getCommentSingleLinePosition(int attrib = 0) const; bool attributeRequiresSpellchecking(int attr); /** * map attribute to its highlighting file. * the returned string is used as key for m_additionalData. */ QString hlKeyForAttrib(int attrib) const; /** * map attribute to its name * @return name of the attribute */ QString nameForAttrib(int attrib) const; /** * Get attribute for the given cursor position. * @param doc document to use * @param cursor cursor position in the given document * @return attribute valid at that location, default is 0 */ int attributeForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor); /** * Get all keywords valid for the given cursor position. * @param doc document to use * @param cursor cursor position in the given document * @return all keywords valid at that location */ QStringList keywordsForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor); /** * Is spellchecking required for the tiven cursor position? * @param doc document to use * @param cursor cursor position in the given document * @return spell checking required? */ bool spellCheckingRequiredForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor); /** * Get highlighting mode for the given cursor position. * @param doc document to use * @param cursor cursor position in the given document * @return mode valid at that location */ QString higlightingModeForLocation(KTextEditor::DocumentPrivate* doc, const KTextEditor::Cursor& cursor); KTextEditor::DefaultStyle defaultStyleForAttribute(int attr) const; void clearAttributeArrays(); QList attributes(const QString &schema); inline bool noHighlighting() const { return noHl; } /** * Indentation mode, e.g. c-style, .... * @return indentation mode */ const QString &indentation() const { return m_indentation; } void getKateExtendedAttributeList(const QString &schema, QList &, KConfig *cfg = nullptr); void getKateExtendedAttributeListCopy(const QString &schema, QList &, KConfig *cfg = nullptr); const QHash &getCharacterEncodings(int attrib) const; const KatePrefixStore &getCharacterEncodingsPrefixStore(int attrib) const; const QHash &getReverseCharacterEncodings(int attrib) const; - int getEncodedCharactersInsertionPolicy(int attrib) const; /** * Returns a list of names of embedded modes. */ QStringList getEmbeddedHighlightingModes() const; private: /** * 'encoding' must not contain new line characters, i.e. '\n' or '\r'! **/ void addCharacterEncoding(const QString &key, const QString &encoding, const QChar &c); /** * create list of attributes from internal formats with properties as defined in syntax file * @return attributes list with attributes as defined in syntax file */ QList attributesForDefinition(); private: QStringList embeddedHighlightingModes; bool noHl = true; bool folding = false; QString iName; QString iSection; bool iHidden = false; QString identifier; QString iStyle; /** * Indentation mode, e.g. c-style, .... */ QString m_indentation; bool m_foldingIndentationSensitive = false; // map schema name to attributes... QHash< QString, QList > m_attributeArrays; /** * This class holds the additional properties for one highlight * definition, such as comment strings, deliminators etc. * * When a highlight is added, a instance of this class is appended to * m_additionalData, and the current position in the attrib and context * arrays are stored in the indexes for look up. You can then use * hlKeyForAttrib to find the relevant instance of this * class from m_additionalData. * * If you need to add a property to a highlight, add it here. */ class HighlightPropertyBag { public: KSyntaxHighlighting::Definition definition; QString singleLineCommentMarker; QString multiLineCommentStart; QString multiLineCommentEnd; KSyntaxHighlighting::CommentPosition singleLineCommentPosition; QVector emptyLines; QHash characterEncodings; KatePrefixStore characterEncodingsPrefixStore; QHash reverseCharacterEncodings; - int encodedCharactersInsertionPolicy; }; /** * Highlight properties for each included highlight definition. * The key is the identifier */ QHash m_additionalData; /** * Fast lookup of hl properties, based on attribute index * The key is the starting index in the attribute array for each file. * @see hlKeyForAttrib */ QMap m_hlIndex; const HighlightPropertyBag &additionalData(const QString &name) const { auto it = m_additionalData.constFind(name); if (it == m_additionalData.constEnd()) it = m_additionalData.constFind(iName); Q_ASSERT(it != m_additionalData.constEnd()); return it.value(); } public: inline bool foldingIndentationSensitive() { return m_foldingIndentationSensitive; } inline bool allowsFolding() { return folding; } /** * all formats for the highlighting definition of this highlighting * includes included formats */ std::vector m_formats; /** * mapping of format id => index into m_formats */ std::unordered_map m_formatsIdToIndex; /** * textline to do updates on during doHighlight */ Kate::TextLineData *m_textLineToHighlight = nullptr; /** * check if the folding begin/ends are balanced! * constructed on demand! */ QHash *m_foldingStartToCount = nullptr; }; #endif