mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 11:52:54 +00:00
411 lines
12 KiB
C++
411 lines
12 KiB
C++
/*
|
|
Copyright 2008 David Nolden <david.nolden.kdevelop@art-master.de>
|
|
|
|
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 <QtCore/qfile.h>
|
|
#include <KDE/KTextEditor/Document>
|
|
#include <language/duchain/indexedstring.h>
|
|
#include <interfaces/idocumentcontroller.h>
|
|
#include <interfaces/icore.h>
|
|
#include <editor/modificationrevision.h>
|
|
#include <ktexteditor/movinginterface.h>
|
|
#include <ktexteditor/configinterface.h>
|
|
|
|
namespace KDevelop {
|
|
|
|
static bool onDiskChangesForbidden = false;
|
|
|
|
QString CodeRepresentation::rangeText(const 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<SimpleRange>& 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
//NOTE: this is ugly, but otherwise kate might remove tabs again :-/
|
|
// see also: https://bugs.kde.org/show_bug.cgi?id=291074
|
|
struct EditorDisableReplaceTabs {
|
|
EditorDisableReplaceTabs(KTextEditor::Document* document) : m_iface(qobject_cast<KTextEditor::ConfigInterface*>(document)), m_count(0) {
|
|
}
|
|
|
|
void start() {
|
|
++m_count;
|
|
if( m_count > 1 )
|
|
return;
|
|
if ( m_iface ) {
|
|
m_oldReplaceTabs = m_iface->configValue( "replace-tabs" );
|
|
m_iface->setConfigValue( "replace-tabs", false );
|
|
}
|
|
}
|
|
|
|
void end() {
|
|
--m_count;
|
|
if( m_count > 0 )
|
|
return;
|
|
|
|
Q_ASSERT( m_count == 0 );
|
|
|
|
if (m_iface)
|
|
m_iface->setConfigValue("replace-tabs", m_oldReplaceTabs);
|
|
}
|
|
|
|
KTextEditor::ConfigInterface* m_iface;
|
|
int m_count;
|
|
QVariant m_oldReplaceTabs;
|
|
};
|
|
|
|
class EditorCodeRepresentation : public DynamicCodeRepresentation {
|
|
public:
|
|
EditorCodeRepresentation(KTextEditor::Document* document) : m_document(document), m_replaceTabs(document) {
|
|
m_url = IndexedString(m_document->url());
|
|
}
|
|
|
|
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(const QString& text) {
|
|
|
|
startEdit();
|
|
bool ret = m_document->setText(text);
|
|
endEdit();
|
|
ModificationRevision::clearModificationCache(m_url);
|
|
return ret;
|
|
}
|
|
|
|
bool fileExists(){
|
|
return QFile(m_document->url().path()).exists();
|
|
}
|
|
|
|
void startEdit() {
|
|
m_document->startEditing();
|
|
m_replaceTabs.start();
|
|
}
|
|
|
|
void endEdit() {
|
|
m_document->endEditing();
|
|
m_replaceTabs.end();
|
|
}
|
|
|
|
bool replace(const KTextEditor::Range& range, const QString& oldText,
|
|
const QString& newText, bool ignoreOldText) {
|
|
QString old = m_document->text(range);
|
|
if(oldText != old && !ignoreOldText) {
|
|
return false;
|
|
}
|
|
|
|
startEdit();
|
|
bool ret = m_document->replaceText(range, newText);
|
|
endEdit();
|
|
|
|
ModificationRevision::clearModificationCache(m_url);
|
|
|
|
return ret;
|
|
}
|
|
|
|
virtual QString rangeText(const KTextEditor::Range& range) const {
|
|
return m_document->text(range);
|
|
}
|
|
|
|
private:
|
|
KTextEditor::Document* m_document;
|
|
IndexedString m_url;
|
|
EditorDisableReplaceTabs m_replaceTabs;
|
|
};
|
|
|
|
class FileCodeRepresentation : public CodeRepresentation {
|
|
public:
|
|
FileCodeRepresentation(const 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 ( 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(const 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(const QString& data) {
|
|
setData(data);
|
|
}
|
|
void setData(const 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<ArtificialStringData> _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(const QString& text) {
|
|
data->setData(text);
|
|
return true;
|
|
}
|
|
|
|
bool fileExists(){
|
|
return false;
|
|
}
|
|
|
|
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<ArtificialStringData> data;
|
|
};
|
|
|
|
static QHash<IndexedString, KSharedPtr<ArtificialStringData> > artificialStrings;
|
|
|
|
//Return the representation for the given URL if it exists, or an empty pointer otherwise
|
|
static KSharedPtr<ArtificialStringData> representationForPath(const IndexedString& path)
|
|
{
|
|
if(artificialStrings.contains(path))
|
|
return artificialStrings[path];
|
|
else
|
|
{
|
|
IndexedString constructedPath(CodeRepresentation::artificialPath(path.str()));
|
|
if(artificialStrings.contains(constructedPath))
|
|
return artificialStrings[constructedPath];
|
|
else
|
|
return KSharedPtr<ArtificialStringData>();
|
|
}
|
|
}
|
|
|
|
bool artificialCodeRepresentationExists(const IndexedString& path)
|
|
{
|
|
return !representationForPath(path).isNull();
|
|
}
|
|
|
|
CodeRepresentation::Ptr createCodeRepresentation(const IndexedString& path) {
|
|
if(artificialCodeRepresentationExists(path))
|
|
return CodeRepresentation::Ptr(new StringCodeRepresentation(representationForPath(path)));
|
|
|
|
IDocument* document = ICore::self()->documentController()->documentForUrl(path.toUrl());
|
|
if(document && document->textDocument())
|
|
return CodeRepresentation::Ptr(new EditorCodeRepresentation(document->textDocument()));
|
|
else
|
|
return CodeRepresentation::Ptr(new FileCodeRepresentation(path));
|
|
}
|
|
|
|
void CodeRepresentation::setDiskChangesForbidden(bool changesForbidden)
|
|
{
|
|
onDiskChangesForbidden = changesForbidden;
|
|
}
|
|
|
|
QString CodeRepresentation::artificialPath(const QString& name)
|
|
{
|
|
KUrl url = KUrl::fromPath(name);
|
|
url.cleanPath();
|
|
|
|
return "/kdev-artificial/" + url.path();
|
|
}
|
|
|
|
InsertArtificialCodeRepresentation::InsertArtificialCodeRepresentation(const IndexedString& file,
|
|
const QString& text)
|
|
: m_file(file)
|
|
{
|
|
if(m_file.toUrl().isRelative())
|
|
{
|
|
m_file = IndexedString(CodeRepresentation::artificialPath(file.str()));
|
|
|
|
int idx = 0;
|
|
while(artificialStrings.contains(m_file))
|
|
{
|
|
++idx;
|
|
m_file = IndexedString(CodeRepresentation::artificialPath(QString("%1_%2").arg(idx).arg(file.str())));
|
|
}
|
|
}
|
|
|
|
Q_ASSERT(!artificialStrings.contains(m_file));
|
|
|
|
artificialStrings.insert(m_file, KSharedPtr<ArtificialStringData>(new ArtificialStringData(text)));
|
|
}
|
|
|
|
IndexedString InsertArtificialCodeRepresentation::file()
|
|
{
|
|
return m_file;
|
|
}
|
|
|
|
InsertArtificialCodeRepresentation::~InsertArtificialCodeRepresentation() {
|
|
Q_ASSERT(artificialStrings.contains(m_file));
|
|
artificialStrings.remove(m_file);
|
|
}
|
|
|
|
void InsertArtificialCodeRepresentation::setText(const 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();
|
|
}
|
|
|
|
}
|
|
|