diff --git a/language/codegen/coderepresentation.cpp b/language/codegen/coderepresentation.cpp index b3aefa7870..05f3d29683 100644 --- a/language/codegen/coderepresentation.cpp +++ b/language/codegen/coderepresentation.cpp @@ -1,349 +1,367 @@ /* Copyright 2008 David Nolden 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. */ #include "coderepresentation.h" #include #include #include #include #include #include #include namespace KDevelop { static bool onDiskChangesForbidden = false; QString CodeRepresentation::rangeText(KTextEditor::Range range) const { Q_ASSERT(range.end().line() < lines()); //Easier for single line ranges which should happen most of the time if(range.onSingleLine()) return QString( line( range.start().line() ).mid( range.start().column(), range.columnWidth() ) ); //Add up al the requested lines QString rangedText = line(range.start().line()).mid(range.start().column()); for(int i = range.start().line() + 1; i <= range.end().line(); ++i) rangedText += '\n' + ((i == range.end().line()) ? line(i).left(range.end().column()) : line(i)); return rangedText; } static void grepLine(const QString& identifier, const QString& lineText, int lineNumber, QVector& ret, bool surroundedByBoundary) { + if (identifier.isEmpty()) + return; + int pos = 0; while(true) { pos = lineText.indexOf(identifier, pos); if(pos == -1) break; int start = pos; pos += identifier.length(); int end = pos; if(!surroundedByBoundary || ( (end == lineText.length() || !lineText[end].isLetterOrNumber() || lineText[end] != '_') && (start-1 < 0 || !lineText[start-1].isLetterOrNumber() || lineText[start-1] != '_')) ) { ret << SimpleRange(lineNumber, start, lineNumber, end); } } } class EditorCodeRepresentation : public DynamicCodeRepresentation { public: EditorCodeRepresentation(KTextEditor::Document* document) : m_document(document) { m_url = IndexedString(m_document->url()); } - virtual QVector< SimpleRange > grep ( QString identifier, bool surroundedByBoundary ) const { + virtual QVector< SimpleRange > grep ( const QString& identifier, bool surroundedByBoundary ) const { QVector< SimpleRange > ret; + + if (identifier.isEmpty()) + return ret; + for(int line = 0; line < m_document->lines(); ++line) grepLine(identifier, m_document->line(line), line, ret, surroundedByBoundary); + return ret; } QString line(int line) const { if(line < 0 || line >= m_document->lines()) return QString(); return m_document->line(line); } virtual int lines() const { return m_document->lines(); } QString text() const { return m_document->text(); } bool setText(QString text) { bool ret = m_document->setText(text); ModificationRevision::clearModificationCache(m_url); return ret; } bool fileExists(){ return QFile(m_document->url().path()).exists(); } void startEdit() { m_document->startEditing(); } void endEdit() { m_document->endEditing(); } bool replace(const KTextEditor::Range& range, QString oldText, QString newText, bool ignoreOldText) { QString old = m_document->text(range); if(oldText != old && !ignoreOldText) { return false; } bool ret = m_document->replaceText(range, newText); ModificationRevision::clearModificationCache(m_url); return ret; } virtual QString rangeText(KTextEditor::Range range) const { return m_document->text(range); } private: KTextEditor::Document* m_document; IndexedString m_url; }; class FileCodeRepresentation : public CodeRepresentation { public: FileCodeRepresentation(IndexedString document) : m_document(document) { QString localFile(document.toUrl().toLocalFile()); QFile file( localFile ); if ( file.open(QIODevice::ReadOnly) ) { data = QString::fromLocal8Bit(file.readAll()); lineData = data.split('\n'); } m_exists = file.exists(); } QString line(int line) const { if(line < 0 || line >= lineData.size()) return QString(); return lineData.at(line); } - virtual QVector< SimpleRange > grep ( QString identifier, bool surroundedByBoundary ) const { + virtual QVector< SimpleRange > grep ( const QString& identifier, bool surroundedByBoundary ) const { QVector< SimpleRange > ret; + + if (identifier.isEmpty()) + return ret; + for(int line = 0; line < lineData.count(); ++line) grepLine(identifier, lineData.at(line), line, ret, surroundedByBoundary); + return ret; } virtual int lines() const { return lineData.count(); } QString text() const { return data; } bool setText(QString text) { Q_ASSERT(!onDiskChangesForbidden); QString localFile(m_document.toUrl().toLocalFile()); QFile file( localFile ); if ( file.open(QIODevice::WriteOnly) ) { QByteArray data = text.toLocal8Bit(); if(file.write(data) == data.size()) { ModificationRevision::clearModificationCache(m_document); return true; } } return false; } bool fileExists(){ return m_exists; } private: //We use QByteArray, because the column-numbers are measured in utf-8 IndexedString m_document; bool m_exists; QStringList lineData; QString data; }; class ArtificialStringData : public QSharedData { public: ArtificialStringData(QString data) { setData(data); } void setData(QString data) { m_data = data; m_lineData = m_data.split('\n'); } QString data() const { return m_data; } const QStringList& lines() const { return m_lineData; } private: QString m_data; QStringList m_lineData; }; class StringCodeRepresentation : public CodeRepresentation { public: StringCodeRepresentation(KSharedPtr _data) : data(_data) { Q_ASSERT(data); } QString line(int line) const { if(line < 0 || line >= data->lines().size()) return QString(); return data->lines().at(line); } virtual int lines() const { return data->lines().count(); } QString text() const { return data->data(); } bool setText(QString text) { data->setData(text); return true; } bool fileExists(){ return false; } - virtual QVector< SimpleRange > grep ( QString identifier, bool surroundedByBoundary ) const { + virtual QVector< SimpleRange > grep ( const QString& identifier, bool surroundedByBoundary ) const { QVector< SimpleRange > ret; + + if (identifier.isEmpty()) + return ret; + for(int line = 0; line < data->lines().count(); ++line) grepLine(identifier, data->lines().at(line), line, ret, surroundedByBoundary); + return ret; } private: KSharedPtr data; }; static QHash > artificialStrings; //Return the representation for the given URL if it exists, or an empty pointer otherwise KSharedPtr representationForUrl(IndexedString url) { if(artificialStrings.contains(url)) return artificialStrings[url]; else { IndexedString constructedUrl(CodeRepresentation::artificialUrl(url.str())); if(artificialStrings.contains(constructedUrl)) return artificialStrings[constructedUrl]; else return KSharedPtr(); } } bool artificialCodeRepresentationExists(IndexedString url) { return !representationForUrl(url).isNull(); } CodeRepresentation::Ptr createCodeRepresentation(IndexedString url) { if(artificialCodeRepresentationExists(url)) return CodeRepresentation::Ptr(new StringCodeRepresentation(representationForUrl(url))); IDocument* document = ICore::self()->documentController()->documentForUrl(url.toUrl()); if(document && document->textDocument()) return CodeRepresentation::Ptr(new EditorCodeRepresentation(document->textDocument())); else return CodeRepresentation::Ptr(new FileCodeRepresentation(url)); } void CodeRepresentation::setDiskChangesForbidden(bool changesForbidden) { onDiskChangesForbidden = changesForbidden; } KUrl CodeRepresentation::artificialUrl(const QString & name) { KUrl url(name); url.setScheme("artificial"); url.cleanPath(); return url; } InsertArtificialCodeRepresentation::InsertArtificialCodeRepresentation(IndexedString file, QString text) : m_file(file) { if(m_file.toUrl().isRelative()) { m_file = IndexedString(CodeRepresentation::artificialUrl(file.str())); int idx = 0; while(artificialStrings.contains(m_file)) { ++idx; m_file = IndexedString(CodeRepresentation::artificialUrl(QString("%1_%2").arg(idx).arg(file.str()))); } } Q_ASSERT(!artificialStrings.contains(m_file)); artificialStrings.insert(m_file, KSharedPtr(new ArtificialStringData(text))); } IndexedString InsertArtificialCodeRepresentation::file() { return m_file; } InsertArtificialCodeRepresentation::~InsertArtificialCodeRepresentation() { Q_ASSERT(artificialStrings.contains(m_file)); artificialStrings.remove(m_file); } void InsertArtificialCodeRepresentation::setText(QString text) { Q_ASSERT(artificialStrings.contains(m_file)); artificialStrings[m_file]->setData(text); } QString InsertArtificialCodeRepresentation::text() { Q_ASSERT(artificialStrings.contains(m_file)); return artificialStrings[m_file]->data(); } } diff --git a/language/codegen/coderepresentation.h b/language/codegen/coderepresentation.h index 2f174f03ce..450cf25b05 100644 --- a/language/codegen/coderepresentation.h +++ b/language/codegen/coderepresentation.h @@ -1,137 +1,137 @@ /* Copyright 2008 David Nolden 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 CODEREPRESENTATION_H #define CODEREPRESENTATION_H #include "../languageexport.h" #include #include class QString; namespace KTextEditor { class Range; } namespace KDevelop { class SimpleRange; class IndexedString; /** * Allows getting code-lines conveniently, either through an open editor, or from a disk-loaded file. */ class KDEVPLATFORMLANGUAGE_EXPORT CodeRepresentation : public QSharedData { public: virtual ~CodeRepresentation() { } virtual QString line(int line) const = 0; virtual int lines() const = 0; virtual QString text() const = 0; virtual QString rangeText(KTextEditor::Range range) const; /** * Search for the given identifier in the document, and returns all ranges * where it was found. * @param identifier The identifier to search for * @param surroundedByBoundary Whether only matches that are surrounded by typical word-boundaries * should be acceded. Everything except letters, numbers, and the _ character * counts as word boundary. * */ - virtual QVector grep(QString identifier, bool surroundedByBoundary = true) const = 0; + virtual QVector grep(const QString& identifier, bool surroundedByBoundary = true) const = 0; /** * Overwrites the text in the file with the new given one * * @return true on success */ virtual bool setText(QString) = 0; /** @return true if this representation represents an actual file on disk */ virtual bool fileExists() = 0; /** * Can be used for example from tests to disallow on-disk changes. When such a change is done, an assertion is triggered. * You should enable this within tests, unless you really want to work on the disk. */ static void setDiskChangesForbidden(bool changesForbidden); /** * Returns the specified name as a url for aritificial source code * suitable for code being inserted into the parser */ static KUrl artificialUrl(const QString & name); typedef KSharedPtr Ptr; }; class KDEVPLATFORMLANGUAGE_EXPORT DynamicCodeRepresentation : public CodeRepresentation { public: /** Used to group edit-history together. Call this before a bunch of replace(), and endEdit in the end. */ virtual void startEdit() = 0; virtual bool replace(const KTextEditor::Range& range, QString oldText, QString newText, bool ignoreOldText = false) = 0; /** Must be called exactly once per startEdit() */ virtual void endEdit() = 0; typedef KSharedPtr Ptr; }; /** * Creates a code-representation for the given url, that allows conveniently accessing its data. Returns zero on failure. */ KDEVPLATFORMLANGUAGE_EXPORT CodeRepresentation::Ptr createCodeRepresentation(IndexedString url); /** * @return true if an artificial code representation already exists for the specified URL */ KDEVPLATFORMLANGUAGE_EXPORT bool artificialCodeRepresentationExists(IndexedString url); /** * Allows inserting artificial source-code into the code-representation and parsing framework. * The source-code logically represents a file. * * The artificial code is automatically removed when the object is destroyed. */ class KDEVPLATFORMLANGUAGE_EXPORT InsertArtificialCodeRepresentation : public QSharedData { public: /** * Inserts an artifial source-code representation with filename @p file and the contents @p text * If @p file is not an absolute path or url, it will be made absolute using the CodeRepresentation::artifialUrl() * function, while ensuring that the name is unique. */ InsertArtificialCodeRepresentation(IndexedString file, QString text); ~InsertArtificialCodeRepresentation(); void setText(QString text); QString text(); /** * Returns the file-name for this code-representation. */ IndexedString file(); private: InsertArtificialCodeRepresentation(const InsertArtificialCodeRepresentation&); InsertArtificialCodeRepresentation& operator=(const InsertArtificialCodeRepresentation&); IndexedString m_file; }; typedef KSharedPtr InsertArtificialCodeRepresentationPointer; } #endif