diff --git a/documentation/documentationview.cpp b/documentation/documentationview.cpp index 5497d203fa..7710c1a077 100644 --- a/documentation/documentationview.cpp +++ b/documentation/documentationview.cpp @@ -1,301 +1,302 @@ /* Copyright 2009 Aleix Pol Gonzalez Copyright 2010 Benjamin Port 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 "documentationview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "documentationfindwidget.h" using namespace KDevelop; DocumentationView::DocumentationView(QWidget* parent) : QWidget(parent) { setWindowIcon(KIcon("documentation")); setLayout(new QVBoxLayout(this)); layout()->setMargin(0); layout()->setSpacing(0); //TODO: clean this up, simply use addAction as that will create a toolbar automatically // use custom KAction's with createWidget for mProviders and mIdentifiers mActions=new KToolBar(this); mActions->setToolButtonStyle(Qt::ToolButtonIconOnly); int iconSize=style()->pixelMetric(QStyle::PM_SmallIconSize); mActions->setIconSize(QSize(iconSize, iconSize)); mFindDoc = new DocumentationFindWidget; mFindDoc->hide(); mBack=mActions->addAction(KIcon("go-previous"), i18n("Back")); mForward=mActions->addAction(KIcon("go-next"), i18n("Forward")); mFind=mActions->addAction(KIcon("edit-find"), i18n("Find"), mFindDoc, SLOT(show())); mActions->addSeparator(); mActions->addAction(KIcon("go-home"), i18n("Home"), this, SLOT(showHome())); mProviders=new QComboBox(mActions); mProviders->setFocusPolicy(Qt::NoFocus); mProvidersModel=new ProvidersModel(mProviders); mProviders->setModel(mProvidersModel); foreach(KDevelop::IDocumentationProvider* p, mProvidersModel->providers()) { connect(dynamic_cast(p), SIGNAL(addHistory(KSharedPtr)), SLOT(addHistory(KSharedPtr))); } connect(mProviders, SIGNAL(activated(int)), SLOT(changedProvider(int))); connect(mProvidersModel, SIGNAL(providersChanged()), this, SLOT(emptyHistory())); mIdentifiers=new KLineEdit(mActions); mIdentifiers->setClearButtonShown(true); mIdentifiers->setCompleter(new QCompleter(mIdentifiers)); // mIdentifiers->completer()->setCompletionMode(QCompleter::UnfilteredPopupCompletion); mIdentifiers->completer()->setCaseSensitivity(Qt::CaseInsensitive); /* vertical size policy should be left to the style. */ mIdentifiers->setSizePolicy(QSizePolicy::Expanding, mIdentifiers->sizePolicy().verticalPolicy()); connect(mIdentifiers, SIGNAL(returnPressed()), SLOT(changedSelection())); connect(mIdentifiers->completer(), SIGNAL(activated(QModelIndex)), SLOT(changeProvider(QModelIndex))); mActions->addWidget(mProviders); mActions->addWidget(mIdentifiers); mBack->setEnabled(false); mForward->setEnabled(false); connect(mBack, SIGNAL(triggered()), this, SLOT(browseBack())); connect(mForward, SIGNAL(triggered()), this, SLOT(browseForward())); mCurrent=mHistory.end(); layout()->addWidget(mActions); layout()->addWidget(new QWidget(this)); layout()->addWidget(mFindDoc); if(mProvidersModel->rowCount()>0) changedProvider(0); } void DocumentationView::browseBack() { mCurrent--; mBack->setEnabled(mCurrent!=mHistory.begin()); mForward->setEnabled(true); updateView(); } void DocumentationView::browseForward() { mCurrent++; mForward->setEnabled(mCurrent+1!=mHistory.end()); mBack->setEnabled(true); updateView(); } void DocumentationView::showHome() { KDevelop::IDocumentationProvider* prov=mProvidersModel->provider(mProviders->currentIndex()); showDocumentation(prov->homePage()); } void DocumentationView::changedSelection() { changeProvider(mIdentifiers->completer()->currentIndex()); } void DocumentationView::changeProvider(const QModelIndex& idx) { if(idx.isValid()) { KDevelop::IDocumentationProvider* prov=mProvidersModel->provider(mProviders->currentIndex()); KSharedPtr doc=prov->documentationForIndex(idx); if(doc) showDocumentation(doc); } } void DocumentationView::showDocumentation(KSharedPtr< KDevelop::IDocumentation > doc) { kDebug(9529) << "showing" << doc->name(); addHistory(doc); updateView(); } void DocumentationView::addHistory(KSharedPtr< KDevelop::IDocumentation > doc) { mBack->setEnabled( !mHistory.isEmpty() ); mForward->setEnabled(false); // clear all history following the current item, unless we're already // at the end (otherwise this code crashes when history is empty, which // happens when addHistory is first called on startup to add the // homepage) if (mCurrent+1 < mHistory.end()) { mHistory.erase(mCurrent+1, mHistory.end()); } mHistory.append(doc); mCurrent=mHistory.end()-1; } void DocumentationView::emptyHistory() { mHistory.clear(); mCurrent=mHistory.end(); mBack->setEnabled(false); mForward->setEnabled(false); if(mProviders->count() > 0) { mProviders->setCurrentIndex(0); + changedProvider(0); } } void DocumentationView::updateView() { mProviders->setCurrentIndex(mProvidersModel->rowForProvider((*mCurrent)->provider())); mIdentifiers->completer()->setModel((*mCurrent)->provider()->indexModel()); mIdentifiers->setText((*mCurrent)->name()); QLayoutItem* lastview=layout()->takeAt(1); Q_ASSERT(lastview); if(lastview->widget()->parent()==this) lastview->widget()->deleteLater(); delete lastview; mFindDoc->setEnabled(false); QWidget* w=(*mCurrent)->documentationWidget(mFindDoc, this); Q_ASSERT(w); mFind->setEnabled(mFindDoc->isEnabled()); if(!mFindDoc->isEnabled()) mFindDoc->hide(); QLayoutItem* findW=layout()->takeAt(1); layout()->addWidget(w); layout()->addItem(findW); } void DocumentationView::changedProvider(int row) { mIdentifiers->completer()->setModel(mProvidersModel->provider(row)->indexModel()); mIdentifiers->clear(); showHome(); } ////////////// ProvidersModel ////////////////// ProvidersModel::ProvidersModel(QObject* parent) : QAbstractListModel(parent) , mProviders(ICore::self()->documentationController()->documentationProviders()) { connect(ICore::self()->pluginController(), SIGNAL(pluginUnloaded(KDevelop::IPlugin*)), SLOT(unloaded(KDevelop::IPlugin*))); connect(ICore::self()->pluginController(), SIGNAL(pluginLoaded(KDevelop::IPlugin*)), SLOT(loaded(KDevelop::IPlugin*))); connect(ICore::self()->documentationController(), SIGNAL(providersChanged()), SLOT(reloadProviders())); } void ProvidersModel::reloadProviders() { beginResetModel(); mProviders = ICore::self()->documentationController()->documentationProviders(); endResetModel(); emit providersChanged(); } QVariant ProvidersModel::data(const QModelIndex& index, int role) const { QVariant ret; switch (role) { case Qt::DisplayRole: ret=provider(index.row())->name(); break; case Qt::DecorationRole: ret=provider(index.row())->icon(); break; } return ret; } void ProvidersModel::unloaded(KDevelop::IPlugin* p) { IDocumentationProvider* prov=p->extension(); int idx=-1; if (prov) idx = mProviders.indexOf(prov); if (idx>=0) { beginRemoveRows(QModelIndex(), idx, idx); mProviders.removeAt(idx); endRemoveRows(); emit providersChanged(); } IDocumentationProviderProvider* provProv=p->extension(); if (provProv) { foreach (IDocumentationProvider* p, provProv->providers()) { idx = mProviders.indexOf(p); if (idx>=0) { beginRemoveRows(QModelIndex(), idx, idx); mProviders.removeAt(idx); endRemoveRows(); } } emit providersChanged(); } } void ProvidersModel::loaded(IPlugin* p) { IDocumentationProvider* prov=p->extension(); if (prov && !mProviders.contains(prov)) { beginInsertRows(QModelIndex(), 0, 0); mProviders.append(prov); endInsertRows(); emit providersChanged(); } IDocumentationProviderProvider* provProv=p->extension(); if (provProv) { beginInsertRows(QModelIndex(), 0, 0); mProviders.append(provProv->providers()); endInsertRows(); emit providersChanged(); } } int ProvidersModel::rowForProvider(IDocumentationProvider* provider) { return mProviders.indexOf(provider); } IDocumentationProvider* ProvidersModel::provider(int pos) const { return mProviders[pos]; } QList< IDocumentationProvider* > ProvidersModel::providers() { return mProviders; } int ProvidersModel::rowCount(const QModelIndex&) const { return mProviders.count(); } diff --git a/interfaces/foregroundlock.cpp b/interfaces/foregroundlock.cpp index 3409b7ba33..0cdfbef610 100644 --- a/interfaces/foregroundlock.cpp +++ b/interfaces/foregroundlock.cpp @@ -1,280 +1,282 @@ /* Copyright 2010 David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "foregroundlock.h" #include #include #include using namespace KDevelop; #define USE_PTHREAD_MUTEX #if defined(USE_PTHREAD_MUTEX) && defined(Q_OS_LINUX) #include #include class SimplePThreadMutex { public: SimplePThreadMutex() { m_mutex = PTHREAD_MUTEX_INITIALIZER; int result = pthread_mutex_init(&m_mutex, 0); Q_ASSERT(result == 0); } ~SimplePThreadMutex() { pthread_mutex_destroy(&m_mutex); } void lock() { int result = pthread_mutex_lock(&m_mutex); Q_ASSERT(result == 0); } void unlock() { int result = pthread_mutex_unlock(&m_mutex); Q_ASSERT(result == 0); } bool tryLock(int interval) { if(interval == 0) { int result = pthread_mutex_trylock(&m_mutex); return result == 0; }else{ timespec abs_time; clock_gettime(CLOCK_REALTIME, &abs_time); abs_time.tv_nsec += interval * 1000000; if(abs_time.tv_nsec >= 1000000000) { abs_time.tv_nsec -= 1000000000; abs_time.tv_sec += 1; } int result = pthread_mutex_timedlock(&m_mutex, &abs_time); return result == 0; } } private: pthread_mutex_t m_mutex; }; namespace { SimplePThreadMutex internalMutex; #else namespace { QMutex internalMutex; #endif QMutex tryLockMutex; QMutex waitMutex; QMutex finishMutex; QWaitCondition condition; volatile QThread* holderThread = 0; volatile int recursion = 0; void lockForegroundMutexInternal() { if(holderThread == QThread::currentThread()) { // We already have the mutex ++recursion; }else{ internalMutex.lock(); Q_ASSERT(recursion == 0 && holderThread == 0); holderThread = QThread::currentThread(); recursion = 1; } } bool tryLockForegroundMutexInternal(int interval = 0) { if(holderThread == QThread::currentThread()) { // We already have the mutex ++recursion; return true; }else{ if(internalMutex.tryLock(interval)) { Q_ASSERT(recursion == 0 && holderThread == 0); holderThread = QThread::currentThread(); recursion = 1; return true; }else{ return false; } } } void unlockForegroundMutexInternal() { Q_ASSERT(holderThread == QThread::currentThread()); Q_ASSERT(recursion > 0); recursion -= 1; if(recursion == 0) { holderThread = 0; internalMutex.unlock(); } } } ForegroundLock::ForegroundLock(bool lock) : m_locked(false) { if(lock) relock(); } void KDevelop::ForegroundLock::relock() { Q_ASSERT(!m_locked); - if(!QApplication::instance() || QThread::currentThread() == QApplication::instance()->thread()) + if(!QApplication::instance() || // Initialization isn't complete yet + QThread::currentThread() == QApplication::instance()->thread() || // We're the main thread (deadlock might happen if we'd enter the trylock loop) + holderThread == QThread::currentThread()) // We already have the foreground lock (deadlock might happen if we'd enter the trylock loop) { lockForegroundMutexInternal(); }else{ QMutexLocker lock(&tryLockMutex); while(!tryLockForegroundMutexInternal(10)) { // In case an additional event-loop was started from within the foreground, we send // events to the foreground to temporarily release the lock. class ForegroundReleaser : public DoInForeground { public: virtual void doInternal() { // By locking the mutex, we make sure that the requester is actually waiting for the condition waitMutex.lock(); // Now we release the foreground lock TemporarilyReleaseForegroundLock release; // And signalize to the requester that we've released it condition.wakeAll(); // Allow the requester to actually wake up, by unlocking m_waitMutex waitMutex.unlock(); // Now wait until the requester is ready QMutexLocker lock(&finishMutex); } }; static ForegroundReleaser releaser; QMutexLocker lockWait(&waitMutex); QMutexLocker lockFinish(&finishMutex); QMetaObject::invokeMethod(&releaser, "doInternalSlot", Qt::QueuedConnection); // We limit the waiting time here, because sometimes it may happen that the foreground-lock is released, // and the foreground is waiting without an event-loop running. (For example through TemporarilyReleaseForegroundLock) condition.wait(&waitMutex, 30); if(tryLockForegroundMutexInternal()) { //success break; }else{ //Probably a third thread has creeped in and //got the foreground lock before us. Just try again. } } } m_locked = true; Q_ASSERT(holderThread == QThread::currentThread()); Q_ASSERT(recursion > 0); } bool KDevelop::ForegroundLock::isLockedForThread() { return QThread::currentThread() == holderThread; } bool KDevelop::ForegroundLock::tryLock() { if(tryLockForegroundMutexInternal()) { m_locked = true; return true; } return false; } void KDevelop::ForegroundLock::unlock() { Q_ASSERT(m_locked); unlockForegroundMutexInternal(); m_locked = false; } TemporarilyReleaseForegroundLock::TemporarilyReleaseForegroundLock() { Q_ASSERT(holderThread == QThread::currentThread()); m_recursion = 0; while(holderThread == QThread::currentThread()) { unlockForegroundMutexInternal(); ++m_recursion; } } TemporarilyReleaseForegroundLock::~TemporarilyReleaseForegroundLock() { for(int a = 0; a < m_recursion; ++a) lockForegroundMutexInternal(); Q_ASSERT(recursion == m_recursion && holderThread == QThread::currentThread()); } KDevelop::ForegroundLock::~ForegroundLock() { if(m_locked) unlock(); } bool KDevelop::ForegroundLock::isLocked() const { return m_locked; } namespace KDevelop { void DoInForeground::doIt() { if(QThread::currentThread() == QApplication::instance()->thread()) { // We're already in the foreground, just call the handler code doInternal(); }else{ QMutexLocker lock(&m_mutex); QMetaObject::invokeMethod(this, "doInternalSlot", Qt::QueuedConnection); m_wait.wait(&m_mutex); } } DoInForeground::~DoInForeground() { } DoInForeground::DoInForeground() { moveToThread(QApplication::instance()->thread()); } void DoInForeground::doInternalSlot() { VERIFY_FOREGROUND_LOCKED doInternal(); QMutexLocker lock(&m_mutex); m_wait.wakeAll(); } } diff --git a/interfaces/kdevelopplugin.desktop b/interfaces/kdevelopplugin.desktop index 126053dc72..a50803ab9d 100644 --- a/interfaces/kdevelopplugin.desktop +++ b/interfaces/kdevelopplugin.desktop @@ -1,106 +1,106 @@ [Desktop Entry] Type=ServiceType X-KDE-ServiceType=KDevelop/Plugin X-KDE-Derived=KPluginInfo Name=KDevelop Plugin Name[bg]=KDevelop приставка Name[ca]=Connector KDevelop Name[ca@valencia]=Connector KDevelop Name[cs]=Modul KDevelop Name[da]=KDevelop-plugin Name[de]=KDevelop-Modul Name[el]=Πρόσθετο του KDevelop Name[en_GB]=KDevelop Plugin Name[es]=Complemento de KDevelop Name[et]=KDevelopi plugin Name[fr]=Module externe pour KDevelop Name[ga]=Breiseán KDevelop Name[gl]=Engadido para KDevelop Name[hr]=KDevelop priključak Name[it]=Estensione KDevelop Name[ja]=KDevelop プラグイン Name[lv]=KDevelop spraudnis Name[nb]=KDevelop programtillegg Name[nds]=KDevelop-Moduul Name[nl]=Plugin van KDevelop Name[pa]=KDevelop ਪਲੱਗਇਨ Name[pl]=Wtyczka do KDevelopa Name[pt]='Plugin' do KDevelop Name[pt_BR]=Extensão do KDevelop Name[ro]=Modul KDevelop -Name[ru]=Модуль KDevelop +Name[ru]=Расширение KDevelop Name[sl]=Vstavek za KDevelop Name[sv]=KDevelop-insticksprogram Name[tr]=KDevelop Eklentisi Name[uk]=Додаток KDevelop Name[x-test]=xxKDevelop Pluginxx Name[zh_CN]=KDevelop 插件 Name[zh_TW]=KDevelop 外掛程式 # mandatory, versioning - prevent DLL hell [PropertyDef::X-KDevelop-Version] Type=int # optional, determines whether a plugin is loaded only after # a project is opened, or is a global plugin. # If it is not set, the plugin can only be loaded by the # user or via requesting one of its dependencies # allowed values: Global, Project [PropertyDef::X-KDevelop-Category] Type=QString # mandatory, GUI-Operation Mode, determines whether a plugin # can work without having a mainwindow/partcontroller # running # allowed values: GUI, NoGUI [PropertyDef::X-KDevelop-Mode] Type=QString # optional, Arguments to pass to the plugin [PropertyDef::X-KDevelop-Args] Type=QString # optional, Plugin type, might be set to Kross # to indicate a Kross plugin [PropertyDef::X-KDevelop-PluginType] Type=QString # optional, Interfaces that a plugin implements # usually values start with org.kdevelop [PropertyDef::X-KDevelop-Interfaces] Type=QStringList # optional, interfaces that this plugin depends # on [PropertyDef::X-KDevelop-IRequired] Type=QStringList # optional, interfaces that this plugin can use, # but the plugin still works if the interfaces are # not available. [PropertyDef::X-KDevelop-IOptional] Type=QStringList # optional, mimetypes supported by a language plugin [PropertyDef::X-KDevelop-SupportedMimeTypes] Type=QStringList # optional, language supported by a language plugin [PropertyDef::X-KDevelop-Language] Type=QString # optional, defines wether the plugin can be disabled # by the user. Possible values are "AlwaysOn" and "UserSelectable". # If the property is missing then UserSelectable is assumed [PropertyDef::X-KDevelop-LoadMode] Type=QString # optional, list of filters for "projectfiles" for the project plugin # For example: Makefile,Makefile.* for Makefile's [PropertyDef::X-KDevelop-ProjectFilesFilter] Type=QStringList # optional, description for the projectfiles filter [PropertyDef::X-KDevelop-ProjectFilesFilterDescription] Type=QString diff --git a/language/codecompletion/codecompletionmodel.cpp b/language/codecompletion/codecompletionmodel.cpp index 263a995e7e..cc67b671f8 100644 --- a/language/codecompletion/codecompletionmodel.cpp +++ b/language/codecompletion/codecompletionmodel.cpp @@ -1,448 +1,453 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2006-2008 Hamish Rodda * Copyright 2007-2008 David Nolden * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "codecompletionmodel.h" #include #include #include #include #include #include #include #include #include #include "../duchain/declaration.h" #include "../duchain/classfunctiondeclaration.h" #include "../duchain/ducontext.h" #include "../duchain/duchain.h" #include "../duchain/namespacealiasdeclaration.h" #include "../duchain/parsingenvironment.h" #include "../duchain/duchainlock.h" #include "../duchain/duchainbase.h" #include "../duchain/topducontext.h" #include "../duchain/duchainutils.h" #include "../interfaces/quickopendataprovider.h" #include "../interfaces/icore.h" #include "../interfaces/ilanguagecontroller.h" #include "../interfaces/icompletionsettings.h" #include "codecompletionworker.h" #include "codecompletioncontext.h" #include using namespace KTextEditor; //Multi-threaded completion creates some multi-threading related crashes, and sometimes shows the completions in the wrong position if the cursor was moved // #define SINGLE_THREADED_COMPLETION namespace KDevelop { class CompletionWorkerThread : public QThread { public: CompletionWorkerThread(CodeCompletionModel* model) : QThread(model), m_model(model), m_worker(m_model->createCompletionWorker()) { + Q_ASSERT(m_worker->parent() == 0); // Must be null, else we cannot change the thread affinity! + m_worker->moveToThread(this); + Q_ASSERT(m_worker->thread() == this); } + ~CompletionWorkerThread() { delete m_worker; } virtual void run () { //We connect directly, so we can do the pre-grouping within the background thread connect(m_worker, SIGNAL(foundDeclarationsReal(QList >, KSharedPtr)), m_model, SLOT(foundDeclarations(QList >, KSharedPtr)), Qt::QueuedConnection); connect(m_model, SIGNAL(completionsNeeded(KDevelop::DUContextPointer, const KTextEditor::Cursor&, KTextEditor::View*)), m_worker, SLOT(computeCompletions(KDevelop::DUContextPointer, const KTextEditor::Cursor&, KTextEditor::View*)), Qt::QueuedConnection); connect(m_model, SIGNAL(doSpecialProcessingInBackground(uint)), m_worker, SLOT(doSpecialProcessing(uint))); exec(); } CodeCompletionModel* m_model; CodeCompletionWorker* m_worker; }; bool CodeCompletionModel::forceWaitForModel() { return m_forceWaitForModel; } void CodeCompletionModel::setForceWaitForModel(bool wait) { m_forceWaitForModel = wait; } CodeCompletionModel::CodeCompletionModel( QObject * parent ) : CodeCompletionModel2(parent) , m_forceWaitForModel(false) , m_fullCompletion(true) , m_mutex(new QMutex) , m_thread(0) { qRegisterMetaType >("QList >"); qRegisterMetaType >("KSharedPtr"); qRegisterMetaType("KTextEditor::Cursor"); } void CodeCompletionModel::initialize() { if(!m_thread) { m_thread = new CompletionWorkerThread(this); #ifdef SINGLE_THREADED_COMPLETION m_thread->m_worker = createCompletionWorker(); #endif m_thread->start(); } } CodeCompletionModel::~CodeCompletionModel() { if(m_thread->m_worker) m_thread->m_worker->abortCurrentCompletion(); m_thread->quit(); m_thread->wait(); delete m_thread; delete m_mutex; } void CodeCompletionModel::addNavigationWidget(const CompletionTreeElement* element, QWidget* widget) const { Q_ASSERT(dynamic_cast(widget)); m_navigationWidgets[element] = widget; } bool CodeCompletionModel::fullCompletion() const { return m_fullCompletion; } KDevelop::CodeCompletionWorker* CodeCompletionModel::worker() const { return m_thread->m_worker; } void CodeCompletionModel::clear() { m_completionItems.clear(); m_navigationWidgets.clear(); m_completionContext.clear(); reset(); } void CodeCompletionModel::completionInvokedInternal(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType, const KUrl& url) { + Q_ASSERT(m_thread == worker()->thread()); Q_UNUSED(invocationType) DUChainReadLocker lock(DUChain::lock(), 400); if( !lock.locked() ) { kDebug() << "could not lock du-chain in time"; return; } TopDUContext* top = DUChainUtils::standardContextForUrl( url ); if(!top) { return; } setCurrentTopContext(TopDUContextPointer(top)); RangeInRevision rangeInRevision = top->transformToLocalRevision(SimpleRange(range)); if (top) { kDebug() << "completion invoked for context" << (DUContext*)top; if( top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->modificationRevision() != ModificationRevision::revisionForFile(IndexedString(url.pathOrUrl())) ) { kDebug() << "Found context is not current. Its revision is " /*<< top->parsingEnvironmentFile()->modificationRevision() << " while the document-revision is " << ModificationRevision::revisionForFile(IndexedString(url.pathOrUrl()))*/; } DUContextPointer thisContext; { kDebug() << "apply specialization:" << range.start(); thisContext = SpecializationStore::self().applySpecialization(top->findContextAt(rangeInRevision.start), top); if ( thisContext ) { kDebug() << "after specialization:" << thisContext->localScopeIdentifier().toString() << thisContext->rangeInCurrentRevision().textRange(); } if(!thisContext) thisContext = top; kDebug() << "context is set to" << thisContext.data(); if( !thisContext ) { kDebug() << "================== NO CONTEXT FOUND ======================="; m_completionItems.clear(); m_navigationWidgets.clear(); reset(); return; } } lock.unlock(); if(m_forceWaitForModel) emit waitForReset(); emit completionsNeeded(thisContext, range.start(), view); } else { kDebug() << "Completion invoked for unknown context. Document:" << url << ", Known documents:" << DUChain::self()->documents(); } } void CodeCompletionModel::completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType) { //If this triggers, initialize() has not been called after creation. Q_ASSERT(m_thread); KDevelop::ICompletionSettings::CompletionLevel level = KDevelop::ICore::self()->languageController()->completionSettings()->completionLevel(); if(level == KDevelop::ICompletionSettings::AlwaysFull || (invocationType != AutomaticInvocation && level == KDevelop::ICompletionSettings::MinimalWhenAutomatic)) m_fullCompletion = true; else m_fullCompletion = false; //Only use grouping in full completion mode setHasGroups(m_fullCompletion); Q_UNUSED(invocationType) if (!worker()) { kWarning() << "Completion invoked on a completion model which has no code completion worker assigned!"; } m_navigationWidgets.clear(); m_completionItems.clear(); reset(); worker()->abortCurrentCompletion(); worker()->setFullCompletion(m_fullCompletion); KUrl url = view->document()->url(); completionInvokedInternal(view, range, invocationType, url); } void CodeCompletionModel::foundDeclarations(QList > items, KSharedPtr completionContext) { m_completionContext = completionContext; if(m_completionItems.isEmpty() && items.isEmpty()) { if(m_forceWaitForModel) reset(); //If we need to reset the model, reset it return; //We don't need to reset, which is bad for target model } m_completionItems = items; if(m_completionContext) { kDebug() << "got completion-context with " << m_completionContext->ungroupedElements().size() << "ungrouped elements"; } reset(); /* if (completionContext == m_completionContext.data()) { if( !m_completionItems.isEmpty() ) { beginInsertRows(QModelIndex(), m_completionItems.count(), m_completionItems.count() + items.count() - 1); m_completionItems += items; endInsertRows(); } else {*/ /* } }*/ } CodeCompletionModelControllerInterface3::MatchReaction CodeCompletionModel::matchingItem(const QModelIndex& matched) { return None; } void CodeCompletionModel::setCompletionContext(KSharedPtr completionContext) { QMutexLocker lock(m_mutex); m_completionContext = completionContext; if(m_completionContext) { kDebug() << "got completion-context with " << m_completionContext->ungroupedElements().size() << "ungrouped elements"; } } KSharedPtr CodeCompletionModel::completionContext() const { QMutexLocker lock(m_mutex); return m_completionContext; } void CodeCompletionModel::executeCompletionItem2(Document* document, const Range& word, const QModelIndex& index) const { //We must not lock the duchain at this place, because the items might rely on that CompletionTreeElement* element = (CompletionTreeElement*)index.internalPointer(); if( !element || !element->asItem() ) return; element->asItem()->execute(document, word); } KSharedPtr< KDevelop::CompletionTreeElement > CodeCompletionModel::itemForIndex(QModelIndex index) const { CompletionTreeElement* element = (CompletionTreeElement*)index.internalPointer(); return KSharedPtr< KDevelop::CompletionTreeElement >(element); } QVariant CodeCompletionModel::data(const QModelIndex& index, int role) const { CompletionTreeElement* element = (CompletionTreeElement*)index.internalPointer(); if( !element ) return QVariant(); CompletionTreeElement& treeElement(*element); if( role == CodeCompletionModel::GroupRole ) { if( treeElement.asNode() ) { return QVariant(treeElement.asNode()->role); }else { kDebug() << "Requested group-role from leaf tree element"; return QVariant(); } }else{ if( treeElement.asNode() ) { if( role == CodeCompletionModel::InheritanceDepth ) { CompletionCustomGroupNode* customGroupNode = dynamic_cast(element); if(customGroupNode) return QVariant(customGroupNode->inheritanceDepth); } if( role == treeElement.asNode()->role ) { return treeElement.asNode()->roleValue; } else { return QVariant(); } } } if(!treeElement.asItem()) { kWarning() << "Error in completion model"; return QVariant(); } //Navigation widget interaction is done here, the other stuff is done within the tree-elements switch (role) { case CodeCompletionModel::InheritanceDepth: return treeElement.asItem()->inheritanceDepth(); case CodeCompletionModel::ArgumentHintDepth: return treeElement.asItem()->argumentHintDepth(); case CodeCompletionModel::ItemSelected: { DeclarationPointer decl = treeElement.asItem()->declaration(); if(decl) { DUChain::self()->emitDeclarationSelected(decl); } break; } } //In minimal completion mode, hide all columns except the "name" one if(!m_fullCompletion && role == Qt::DisplayRole && index.column() != Name && (treeElement.asItem()->argumentHintDepth() == 0 || index.column() == Prefix)) return QVariant(); QVariant ret = treeElement.asItem()->data(index, role, this); //In reduced completion mode, don't show information text with the selected items if(role == ItemSelected && (!m_fullCompletion || !ICore::self()->languageController()->completionSettings()->showMultiLineSelectionInformation())) return QVariant(); return ret; } KDevelop::TopDUContextPointer CodeCompletionModel::currentTopContext() const { return m_currentTopContext; } void CodeCompletionModel::setCurrentTopContext(KDevelop::TopDUContextPointer topContext) { m_currentTopContext = topContext; } QModelIndex CodeCompletionModel::index(int row, int column, const QModelIndex& parent) const { if( parent.isValid() ) { CompletionTreeElement* element = (CompletionTreeElement*)parent.internalPointer(); CompletionTreeNode* node = element->asNode(); if( !node ) { kDebug() << "Requested sub-index of leaf node"; return QModelIndex(); } if (row < 0 || row >= node->children.count() || column < 0 || column >= ColumnCount) return QModelIndex(); return createIndex(row, column, node->children[row].data()); } else { if (row < 0 || row >= m_completionItems.count() || column < 0 || column >= ColumnCount) return QModelIndex(); return createIndex(row, column, const_cast(m_completionItems[row].data())); } } QModelIndex CodeCompletionModel::parent ( const QModelIndex & index ) const { if(rowCount() == 0) return QModelIndex(); if( index.isValid() ) { CompletionTreeElement* element = (CompletionTreeElement*)index.internalPointer(); if( element->parent() ) return createIndex( element->rowInParent(), element->columnInParent(), element->parent() ); } return QModelIndex(); } int CodeCompletionModel::rowCount ( const QModelIndex & parent ) const { if( parent.isValid() ) { CompletionTreeElement* element = (CompletionTreeElement*)parent.internalPointer(); CompletionTreeNode* node = element->asNode(); if( !node ) return 0; return node->children.count(); }else{ return m_completionItems.count(); } } QString CodeCompletionModel::filterString(KTextEditor::View* view, const KTextEditor::Range& range, const KTextEditor::Cursor& position) { m_filterString = KTextEditor::CodeCompletionModelControllerInterface3::filterString(view, range, position); return m_filterString; } } #include "codecompletionmodel.moc" diff --git a/language/codecompletion/codecompletionmodel.h b/language/codecompletion/codecompletionmodel.h index e2fab982b3..b220905138 100644 --- a/language/codecompletion/codecompletionmodel.h +++ b/language/codecompletion/codecompletionmodel.h @@ -1,146 +1,147 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2006-2008 Hamish Rodda * Copyright 2007-2008 David Nolden * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEV_CODECOMPLETIONMODEL_H #define KDEV_CODECOMPLETIONMODEL_H #include #include #include #include #include #include "../duchain/duchainpointer.h" #include "../languageexport.h" #include "codecompletioncontext.h" #include "codecompletionitem.h" #include #include #include class QIcon; class QString; class QMutex; namespace KDevelop { class DUContext; class Declaration; class CodeCompletionWorker; class CompletionWorkerThread; class KDEVPLATFORMLANGUAGE_EXPORT CodeCompletionModel : public KTextEditor::CodeCompletionModel2 , public KTextEditor::CodeCompletionModelControllerInterface3 { Q_OBJECT Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface3) public: CodeCompletionModel(QObject* parent); virtual ~CodeCompletionModel(); ///This MUST be called after the creation of this completion-model. ///If you use use the KDevelop::CodeCompletion helper-class, that one cares about it. virtual void initialize(); ///Entry-point for code-completion. This determines ome settings, clears the model, and then calls completionInvokedInternal for further processing. virtual void completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType); virtual QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; virtual QModelIndex parent ( const QModelIndex & index ) const; ///Use this to set whether the code-completion widget should wait for this model until it's shown ///This makes sense when the model takes some time but not too much time, to make the UI less flickering and ///annoying. ///The default is false void setForceWaitForModel(bool wait); bool forceWaitForModel(); ///Convenience-storage for use by the inherited completion model void setCompletionContext(KSharedPtr completionContext); KSharedPtr completionContext() const; ///Convenience-storage for use by the inherited completion model KDevelop::TopDUContextPointer currentTopContext() const; void setCurrentTopContext(KDevelop::TopDUContextPointer topContext); ///Tracks navigation widget so they can be interactive with through the keyboard later on void addNavigationWidget(const CompletionTreeElement* element, QWidget* widget) const; ///Whether the completion should be fully detailed. If false, it should be simplifed, so no argument-hints, ///no expanding information, no type-information, etc. bool fullCompletion() const; virtual MatchReaction matchingItem(const QModelIndex& matched); virtual QString filterString(KTextEditor::View* view, const KTextEditor::Range& range, const KTextEditor::Cursor& position); void clear(); ///Returns the tree-element that belogns to the index, or zero KSharedPtr itemForIndex(QModelIndex index) const; Q_SIGNALS: ///Connection from this completion-model into the background worker thread. You should emit this from within completionInvokedInternal. void completionsNeeded(KDevelop::DUContextPointer context, const KTextEditor::Cursor& position, KTextEditor::View* view); ///Additional signal that allows directly stepping into the worker-thread, bypassing computeCompletions(..) etc. ///doSpecialProcessing(data) will be executed in the background thread. void doSpecialProcessingInBackground(uint data); protected Q_SLOTS: ///Connection from the background-thread into the model: This is called when the background-thread is ready virtual void foundDeclarations(QList > item, KSharedPtr completionContext); protected: ///Eventually override this, determine the context or whatever, and then emit completionsNeeded(..) to continue processing in the background tread. ///The default-implementation does this completely, so if you don't need to do anything special, you can just leave it. virtual void completionInvokedInternal(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType, const KUrl& url); virtual void executeCompletionItem2(KTextEditor::Document* document, const KTextEditor::Range& word, const QModelIndex& index) const; KSharedPtr m_completionContext; typedef QPair > DeclarationContextPair; mutable QMap > m_navigationWidgets; QList< KSharedPtr > m_completionItems; - //Should create a completion-worker. The worker must be created right within this function, so it is assigned to the correct rhread. + /// Should create a completion-worker. The worker must have no parent object, + /// because else its thread-affinity can not be changed. virtual CodeCompletionWorker* createCompletionWorker() = 0; friend class CompletionWorkerThread; CodeCompletionWorker* worker() const; private: bool m_forceWaitForModel; bool m_fullCompletion; QMutex* m_mutex; CompletionWorkerThread* m_thread; QString m_filterString; KDevelop::TopDUContextPointer m_currentTopContext; }; } #endif diff --git a/language/codecompletion/codecompletionworker.cpp b/language/codecompletion/codecompletionworker.cpp index 3ab9cb46b5..ea6e1adf60 100644 --- a/language/codecompletion/codecompletionworker.cpp +++ b/language/codecompletion/codecompletionworker.cpp @@ -1,210 +1,223 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2006-2008 Hamish Rodda * Copyright 2007-2008 David Nolden * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "codecompletionworker.h" #include #include #include #include #include "../duchain/ducontext.h" #include "../duchain/duchainlock.h" #include "../duchain/duchain.h" #include "codecompletion.h" #include "codecompletionitem.h" #include "codecompletionmodel.h" #include "codecompletionitemgrouper.h" #include using namespace KTextEditor; using namespace KDevelop; -CodeCompletionWorker::CodeCompletionWorker(QObject* parent) : - QObject(parent) - , m_hasFoundDeclarations(false) +CodeCompletionWorker::CodeCompletionWorker(CodeCompletionModel* model) : + m_hasFoundDeclarations(false) , m_mutex(new QMutex()) , m_abort(false) , m_fullCompletion(true) + , m_model(model) { } CodeCompletionWorker::~CodeCompletionWorker() { delete m_mutex; } void CodeCompletionWorker::setFullCompletion(bool full) { m_fullCompletion = full; } bool CodeCompletionWorker::fullCompletion() const { return m_fullCompletion; } void CodeCompletionWorker::failed() { foundDeclarations( QList >(), KSharedPtr() ); } void KDevelop::CodeCompletionWorker::foundDeclarations(QList< KSharedPtr< KDevelop::CompletionTreeElement > > items, KSharedPtr< KDevelop::CodeCompletionContext > completionContext) { m_hasFoundDeclarations = true; emit foundDeclarationsReal(items, completionContext); } void CodeCompletionWorker::computeCompletions(KDevelop::DUContextPointer context, const KTextEditor::Cursor& position, KTextEditor::View* view) { { QMutexLocker lock(m_mutex); m_abort = false; } + ///@todo It's not entirely safe to pass KTextEditor::View* through a queued connection + // We access the view/document which is not thread-safe, so we need the foreground lock + ForegroundLock foreground; + //Compute the text we should complete on KTextEditor::Document* doc = view->document(); if( !doc ) { kDebug() << "No document for completion"; failed(); return; } KTextEditor::Range range; QString text; { - { - ForegroundLock lockForeground; - QMutexLocker lock(m_mutex); - DUChainReadLocker lockDUChain; - - if(context) { - kDebug() << context->localScopeIdentifier().toString(); - range = KTextEditor::Range(context->rangeInCurrentRevision().start.textCursor(), position); - } - else - range = KTextEditor::Range(KTextEditor::Cursor(position.line(), 0), position); - - text = doc->text(range); + QMutexLocker lock(m_mutex); + DUChainReadLocker lockDUChain; + + if(context) { + kDebug() << context->localScopeIdentifier().toString(); + range = KTextEditor::Range(context->rangeInCurrentRevision().start.textCursor(), position); } + else + range = KTextEditor::Range(KTextEditor::Cursor(position.line(), 0), position); + + updateContextRange(range, view, context); + + text = doc->text(range); } if( position.column() == 0 ) //Seems like when the cursor is a the beginning of a line, kate does not give the \n text += '\n'; if (aborting()) { failed(); return; } m_hasFoundDeclarations = false; + + KTextEditor::Cursor cursorPosition = view->cursorPosition(); + QString followingText; //followingText may contain additional text that stands for the current item. For example in the case "QString|", QString is in addedText. + if(position < cursorPosition) + followingText = view->document()->text( KTextEditor::Range( position, cursorPosition ) ); - computeCompletions(context, position, view, range, text); + foreground.unlock(); + + computeCompletions(context, position, followingText, range, text); if(!m_hasFoundDeclarations) failed(); } void KDevelop::CodeCompletionWorker::doSpecialProcessing(uint) { } CodeCompletionContext* CodeCompletionWorker::createCompletionContext(KDevelop::DUContextPointer context, const QString& contextText, const QString& followingText, const KDevelop::CursorInRevision& position) const { Q_UNUSED(context); Q_UNUSED(contextText); Q_UNUSED(followingText); Q_UNUSED(position); return 0; } -void CodeCompletionWorker::computeCompletions(KDevelop::DUContextPointer context, const KTextEditor::Cursor& position, KTextEditor::View* view, const KTextEditor::Range& contextRange, const QString& contextText) +void CodeCompletionWorker::computeCompletions(KDevelop::DUContextPointer context, const KTextEditor::Cursor& position, QString followingText, const KTextEditor::Range& contextRange, const QString& contextText) { Q_UNUSED(contextRange); - KTextEditor::Cursor cursorPosition = view->cursorPosition(); - QString followingText; //followingText may contain additional text that stands for the current item. For example in the case "QString|", QString is in addedText. - if(position < cursorPosition) - followingText = view->document()->text( KTextEditor::Range( position, cursorPosition ) ); kDebug() << "added text:" << followingText; CodeCompletionContext::Ptr completionContext( createCompletionContext( context, contextText, followingText, CursorInRevision::castFromSimpleCursor(SimpleCursor(position)) ) ); if (KDevelop::CodeCompletionModel* m = model()) m->setCompletionContext(KDevelop::CodeCompletionContext::Ptr::staticCast(completionContext)); if( completionContext && completionContext->isValid() ) { DUChainReadLocker lock(DUChain::lock()); if (!context) { failed(); kDebug() << "Completion context disappeared before completions could be calculated"; return; } QList items = completionContext->completionItems(aborting(), fullCompletion()); if (aborting()) { failed(); return; } QList > tree = computeGroups( items, completionContext ); if(aborting()) { failed(); return; } tree += completionContext->ungroupedElements(); foundDeclarations( tree, KSharedPtr::staticCast(completionContext) ); } else { kDebug() << "setContext: Invalid code-completion context"; } } QList > CodeCompletionWorker::computeGroups(QList items, KSharedPtr completionContext) { Q_UNUSED(completionContext); QList > tree; /** * 1. Group by argument-hint depth * 2. Group by inheritance depth * 3. Group by simplified attributes * */ CodeCompletionItemGrouper > > argumentHintDepthGrouper(tree, 0, items); return tree; } void CodeCompletionWorker::abortCurrentCompletion() { QMutexLocker lock(m_mutex); m_abort = true; } bool& CodeCompletionWorker::aborting() { return m_abort; } KDevelop::CodeCompletionModel* CodeCompletionWorker::model() const { - return const_cast(static_cast(parent())); + return m_model; +} + +void CodeCompletionWorker::updateContextRange(Range& contextRange, View* view, DUContextPointer context) const +{ + Q_UNUSED(contextRange); + Q_UNUSED(view); + Q_UNUSED(context); } #include "codecompletionworker.moc" diff --git a/language/codecompletion/codecompletionworker.h b/language/codecompletion/codecompletionworker.h index 6b45b454c9..8e150486bf 100644 --- a/language/codecompletion/codecompletionworker.h +++ b/language/codecompletion/codecompletionworker.h @@ -1,106 +1,111 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2006-2008 Hamish Rodda * Copyright 2007-2008 David Nolden * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEV_CODECOMPLETIONWORKER_H #define KDEV_CODECOMPLETIONWORKER_H #include #include #include #include #include "../languageexport.h" #include "../duchain/duchainpointer.h" #include "../codecompletion/codecompletioncontext.h" class QMutex; namespace KTextEditor { class Range; class View; class Cursor; } namespace KDevelop { class CodeCompletion; class CompletionTreeElement; class CodeCompletionModel; class KDEVPLATFORMLANGUAGE_EXPORT CodeCompletionWorker : public QObject { Q_OBJECT public: - CodeCompletionWorker(QObject* parent); + CodeCompletionWorker(CodeCompletionModel* model); virtual ~CodeCompletionWorker(); virtual void abortCurrentCompletion(); void setFullCompletion(bool); bool fullCompletion() const; KDevelop::CodeCompletionModel* model() const; ///When this is called, the result is shown in the completion-list. ///Call this from within your code void foundDeclarations(QList >, KSharedPtr completionContext); Q_SIGNALS: ///Internal connections into the foreground completion model void foundDeclarationsReal(QList >, KSharedPtr completionContext); protected: - virtual void computeCompletions(DUContextPointer context, const KTextEditor::Cursor& position, KTextEditor::View* view, const KTextEditor::Range& contextRange, const QString& contextText); + virtual void computeCompletions(DUContextPointer context, const KTextEditor::Cursor& position, QString followingText, const KTextEditor::Range& contextRange, const QString& contextText); virtual QList > computeGroups(QList items, KSharedPtr completionContext); ///If you don't need to reimplement computeCompletions, you can implement only this. virtual KDevelop::CodeCompletionContext* createCompletionContext(KDevelop::DUContextPointer context, const QString &contextText, const QString &followingText, const CursorInRevision &position) const; + ///Override this to change the text-range which is used as context-information for the completion context + ///The foreground-lock and a DUChain read lock are held when this is called + virtual void updateContextRange(KTextEditor::Range& contextRange, KTextEditor::View* view, DUContextPointer context) const; + ///Can be used to retrieve and set the aborting flag(Enabling it is equivalent to caling abortCompletion()) ///Is always reset from within computeCompletions bool& aborting(); ///Emits foundDeclarations() with an empty list. Always call this when you abort the process of computing completions void failed(); - + public Q_SLOTS: ///Connection from the foreground thread within CodeCompletionModel void computeCompletions(KDevelop::DUContextPointer context, const KTextEditor::Cursor& position, KTextEditor::View* view); ///This can be used to do special processing within the background, completely bypassing the normal computeCompletions(..) etc. system. ///It will be executed within the background when the model emits doSpecialProcessingInBackground virtual void doSpecialProcessing(uint data); private: bool m_hasFoundDeclarations; QMutex* m_mutex; bool m_abort; bool m_fullCompletion; + KDevelop::CodeCompletionModel* m_model; }; } #endif // KDEVCODECOMPLETIONWORKER_H diff --git a/language/codegen/createclass.cpp b/language/codegen/createclass.cpp index cd20e151bf..976f4c304e 100644 --- a/language/codegen/createclass.cpp +++ b/language/codegen/createclass.cpp @@ -1,746 +1,758 @@ /* This file is part of KDevelop Copyright 2008 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. */ #include "createclass.h" #include #include #include #include #include #include #include #include "ui_newclass.h" #include "ui_licensechooser.h" #include "ui_outputlocation.h" #include "duchain/persistentsymboltable.h" #include "duchain/duchainlock.h" #include "duchain/duchain.h" #include "duchain/types/structuretype.h" #include "codegen/documentchangeset.h" #include "overridespage.h" #include #include #include #include +#include namespace KDevelop { class CreateClassWizardPrivate { public: KUrl baseUrl; OutputPage* output; ClassGenerator * generator; }; CreateClassWizard::CreateClassWizard(QWidget* parent, ClassGenerator * generator, KUrl baseUrl) : QWizard(parent) , d(new CreateClassWizardPrivate) { d->baseUrl = baseUrl; Q_ASSERT(generator); d->generator = generator; setDefaultProperty("KUrlRequester", "url", SIGNAL(textChanged(QString))); setDefaultProperty("KTextEdit", "plainText", SIGNAL(textChanged())); } CreateClassWizard::~CreateClassWizard() { delete d; } void CreateClassWizard::setup() { setWindowTitle(i18n("Create New Class in %1", d->baseUrl.prettyUrl())); if (QWizardPage* page = newIdentifierPage()) addPage(page); if (QWizardPage* page = newOverridesPage()) addPage(page); addPage(new LicensePage(this)); addPage(d->output = new OutputPage(this)); } void CreateClassWizard::accept() { QWizard::accept(); //Transmit all the final information to the generator d->generator->setLicense(field("license").toString()); kDebug() << "Header Url: " << field("headerUrl").toString(); /* d->generator->setHeaderUrl(field("headerUrl").toString()); d->generator->setImplementationUrl(field("implementationUrl").toString()); d->generator->setHeaderPosition(SimpleCursor(field("headerLine").toInt(), field("headerColumn").toInt())); d->generator->setHeaderPosition(SimpleCursor(field("implementationLine").toInt(), field("implementationColumn").toInt())); */ DocumentChangeSet changes = d->generator->generate(); changes.setReplacementPolicy(DocumentChangeSet::WarnOnFailedChange); changes.setActivationPolicy(KDevelop::DocumentChangeSet::Activate); changes.applyAllChanges(); } ClassGenerator * CreateClassWizard::generator() { return d->generator; } ClassIdentifierPage* CreateClassWizard::newIdentifierPage() { return new ClassIdentifierPage(this); } OverridesPage* CreateClassWizard::newOverridesPage() { return new OverridesPage(d->generator, this); } struct ClassGeneratorPrivate { QString name; ///< The name for the class to be generated (does not include namespace if relevant) QString license; QList inheritedClasses; ///< Represent *ALL* of the inherited classes SimpleCursor headerPosition; SimpleCursor implementationPosition; KUrl headerUrl; KUrl implementationUrl; }; ClassGenerator::ClassGenerator() : d(new ClassGeneratorPrivate) { } ClassGenerator::~ClassGenerator() { delete d; } const QString & ClassGenerator::name() const { return d->name; } void ClassGenerator::setName(const QString & newName) { d->name = newName; } QString ClassGenerator::identifier() const { return name(); } void ClassGenerator::setIdentifier(const QString & identifier) { setName(identifier); } void ClassGenerator::addDeclaration(DeclarationPointer newDeclaration) { m_declarations << newDeclaration; } const QList ClassGenerator::declarations() const { return m_declarations; } const QList & ClassGenerator::addBaseClass(const QString & newBaseClass) { DUChainReadLocker lock(DUChain::lock()); bool added = false; PersistentSymbolTable::Declarations decl = PersistentSymbolTable::self().getDeclarations(IndexedQualifiedIdentifier(QualifiedIdentifier(newBaseClass))); //Search for all super classes for(PersistentSymbolTable::Declarations::Iterator it = decl.iterator(); it; ++it) { DeclarationPointer declaration = DeclarationPointer(it->declaration()); if(declaration->isForwardDeclaration()) continue; // Check if it's a class/struct/etc if(declaration->type()) { fetchSuperClasses(declaration); m_baseClasses << declaration; added = true; break; } } if(!added) m_baseClasses << DeclarationPointer(); //Some entities expect that there is always an item added to the list, so just add zero return m_baseClasses; } const QList & ClassGenerator::inheritanceList() const { return d->inheritedClasses; } const QList< DeclarationPointer >& ClassGenerator::directInheritanceList() const { return m_baseClasses; } void ClassGenerator::clearInheritance() { m_baseClasses.clear(); d->inheritedClasses.clear(); } void ClassGenerator::clearDeclarations() { m_declarations.clear(); } KUrl ClassGenerator::headerUrlFromBase(KUrl baseUrl, bool toLower) { Q_UNUSED(baseUrl); Q_UNUSED(toLower); KUrl url; url.addPath(d->name); return url; } KUrl ClassGenerator::implementationUrlFromBase(KUrl baseUrl, bool toLower) { Q_UNUSED(baseUrl); Q_UNUSED(toLower); return KUrl(); } void ClassGenerator::setHeaderPosition ( SimpleCursor position ) { d->headerPosition = position; } void ClassGenerator::setImplementationPosition ( SimpleCursor position ) { d->implementationPosition = position; } void ClassGenerator::setHeaderUrl ( KUrl header ) { d->headerUrl = header; kDebug() << "Header for the generated class: " << header; } void ClassGenerator::setImplementationUrl ( KUrl implementation ) { d->implementationUrl = implementation; kDebug() << "Implementation for the generated class: " << implementation; } SimpleCursor ClassGenerator::headerPosition() { return d->headerPosition; } SimpleCursor ClassGenerator::implementationPosition() { return d->implementationPosition; } KUrl & ClassGenerator::headerUrl() { return d->headerUrl; } KUrl & ClassGenerator::implementationUrl() { return d->implementationUrl; } /// Specify license for this class void ClassGenerator::setLicense(const QString & license) { kDebug() << "New Class: " << d->name << "Set license: " << d->license; d->license = license; } /// Get the license specified for this classes const QString & ClassGenerator::license() const { return d->license; } void ClassGenerator::fetchSuperClasses(DeclarationPointer derivedClass) { DUChainReadLocker lock(DUChain::lock()); //Prevent duplicity if(d->inheritedClasses.contains(derivedClass)) return; d->inheritedClasses.append(derivedClass); DUContext* context = derivedClass->internalContext(); foreach (const DUContext::Import& import, context->importedParentContexts()) if (DUContext * parentContext = import.context(context->topContext())) if (parentContext->type() == DUContext::Class) fetchSuperClasses( DeclarationPointer(parentContext->owner()) ); } class ClassIdentifierPagePrivate { public: ClassIdentifierPagePrivate() : classid(0) { } Ui::NewClassDialog* classid; }; ClassIdentifierPage::ClassIdentifierPage(QWizard* parent) : QWizardPage(parent) , d(new ClassIdentifierPagePrivate) { setTitle(i18n("Class Basics")); setSubTitle( i18n("Identify the class and any classes from which it is to inherit.") ); d->classid = new Ui::NewClassDialog; d->classid->setupUi(this); d->classid->addInheritancePushButton->setIcon(KIcon("list-add")); d->classid->removeInheritancePushButton->setIcon(KIcon("list-remove")); d->classid->moveDownPushButton->setIcon(KIcon("go-down")); d->classid->moveUpPushButton->setIcon(KIcon("go-up")); connect(d->classid->addInheritancePushButton, SIGNAL(pressed()), this, SLOT(addInheritance())); connect(d->classid->removeInheritancePushButton, SIGNAL(pressed()), this, SLOT(removeInheritance())); connect(d->classid->moveUpPushButton, SIGNAL(pressed()), this, SLOT(moveUpInheritance())); connect(d->classid->moveDownPushButton, SIGNAL(pressed()), this, SLOT(moveDownInheritance())); connect(d->classid->inheritanceList, SIGNAL(currentRowChanged(int)), this, SLOT(checkMoveButtonState())); registerField("classIdentifier*", d->classid->identifierLineEdit); registerField("classInheritance", this, "inheritance", SIGNAL(inheritanceChanged())); } ClassIdentifierPage::~ClassIdentifierPage() { delete d; } KLineEdit* ClassIdentifierPage::identifierLineEdit() const { return d->classid->identifierLineEdit; } KLineEdit* ClassIdentifierPage::inheritanceLineEdit() const { return d->classid->inheritanceLineEdit; } void ClassIdentifierPage::addInheritance() { d->classid->inheritanceList->addItem(d->classid->inheritanceLineEdit->text()); d->classid->inheritanceLineEdit->clear(); d->classid->removeInheritancePushButton->setEnabled(true); if (d->classid->inheritanceList->count() > 1) checkMoveButtonState(); emit inheritanceChanged(); } void ClassIdentifierPage::removeInheritance() { delete d->classid->inheritanceList->takeItem(d->classid->inheritanceList->currentRow()); if (d->classid->inheritanceList->count() == 0) d->classid->removeInheritancePushButton->setEnabled(false); checkMoveButtonState(); emit inheritanceChanged(); } void ClassIdentifierPage::moveUpInheritance() { int currentRow = d->classid->inheritanceList->currentRow(); Q_ASSERT(currentRow > 0); if (currentRow <= 0) return; QListWidgetItem* item = d->classid->inheritanceList->takeItem(currentRow); d->classid->inheritanceList->insertItem(currentRow - 1, item); d->classid->inheritanceList->setCurrentItem(item); emit inheritanceChanged(); } void ClassIdentifierPage::moveDownInheritance() { int currentRow = d->classid->inheritanceList->currentRow(); Q_ASSERT(currentRow != -1 && currentRow < d->classid->inheritanceList->count() - 1); if (currentRow == -1 || currentRow >= d->classid->inheritanceList->count() - 1) return; QListWidgetItem* item = d->classid->inheritanceList->takeItem(currentRow); d->classid->inheritanceList->insertItem(currentRow + 1, item); d->classid->inheritanceList->setCurrentItem(item); emit inheritanceChanged(); } QualifiedIdentifier ClassIdentifierPage::parseParentClassId(const QString& inheritedObject) { return QualifiedIdentifier(inheritedObject); } void ClassIdentifierPage::checkMoveButtonState() { int currentRow = d->classid->inheritanceList->currentRow(); d->classid->moveUpPushButton->setEnabled(currentRow > 0); d->classid->moveDownPushButton->setEnabled(currentRow >= 0 && currentRow < d->classid->inheritanceList->count() - 1); } QStringList ClassIdentifierPage::inheritanceList() const { QStringList ret; for (int i = 0; i < d->classid->inheritanceList->count(); ++i) ret << d->classid->inheritanceList->item(i)->text(); return ret; } bool ClassIdentifierPage::validatePage ( void ) { //save the information in the generator ClassGenerator * generator = dynamic_cast(wizard())->generator(); generator->setIdentifier(field("classIdentifier").toString()); //Remove old base classes, and add the new ones generator->clearInheritance(); foreach (const QString & inherited, field("classInheritance").toStringList()) generator->addBaseClass(inherited); return true; } class LicensePagePrivate { public: struct LicenseInfo { QString name; QString path; QString contents; }; typedef QList LicenseList; LicensePagePrivate() : license(0) { } Ui::LicenseChooserDialog* license; LicenseList availableLicenses; }; LicensePage::LicensePage(QWizard* parent) : QWizardPage(parent) , d(new LicensePagePrivate) { setTitle(i18n("License")); setSubTitle( i18n("Choose the license under which to place the new class.") ); d->license = new Ui::LicenseChooserDialog; d->license->setupUi(this); connect(d->license->licenseComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(licenseComboChanged(int))); connect(d->license->saveLicense, SIGNAL(clicked(bool)), d->license->licenseName, SLOT(setEnabled(bool))); // Read all the available licenses from the standard dirs initializeLicenses(); //Set the license selection to the previous one KConfigGroup config(KGlobal::config()->group("CodeGeneration")); d->license->licenseComboBox->setCurrentIndex(config.readEntry( "LastSelectedLicense", 0 )); //Needed to avoid a bug where licenseComboChanged doesn't get called by QComboBox if the past selection was 0 licenseComboChanged(d->license->licenseComboBox->currentIndex()); registerField("license", d->license->licenseTextEdit); } LicensePage::~LicensePage() { KConfigGroup config(KGlobal::config()->group("CodeGeneration")); //Do not save invalid license numbers' int index = d->license->licenseComboBox->currentIndex(); if( index >= 0 || index < d->availableLicenses.size() ) { config.writeEntry("LastSelectedLicense", index); config.config()->sync(); } else kWarning() << "Attempted to save an invalid license number: " << index << ". Number of licenses:" << d->availableLicenses.size(); delete d; } // If the user entered a custom license that they want to save, save it bool LicensePage::validatePage() { if(d->license->licenseComboBox->currentIndex() == (d->availableLicenses.size() - 1) && d->license->saveLicense->isChecked()) return saveLicense(); else return true; } //! Read all the license files in the global and local config dirs void LicensePage::initializeLicenses() { kDebug() << "Searching for available licenses"; KStandardDirs * dirs = KGlobal::dirs(); QStringList licenseDirs = dirs->findDirs("data", "kdevcodegen/licenses"); //Iterate through the possible directories that contain licenses, and load their names foreach(const QString& currentDir, licenseDirs) { QDirIterator it(currentDir, QDir::Files | QDir::Readable); while(it.hasNext()) { LicensePagePrivate::LicenseInfo newLicense; newLicense.path = it.next(); newLicense.name = it.fileName(); kDebug() << "Found License: " << newLicense.name; d->availableLicenses.push_back(newLicense); d->license->licenseComboBox->addItem(newLicense.name); } } //Finally add the option other for user specified licenses LicensePagePrivate::LicenseInfo license; d->availableLicenses.push_back(license); d->license->licenseComboBox->addItem("Other"); } // Read a license index, if it is not loaded, open it from the file QString & LicensePage::readLicense(int licenseIndex) { //If the license is not loaded into memory, read it in if(d->availableLicenses[licenseIndex].contents.isEmpty()) { QString licenseText(""); //If we are dealing with the last option "other" just return a new empty string if(licenseIndex != (d->availableLicenses.size() - 1)) { kDebug() << "Reading license: " << d->availableLicenses[licenseIndex].name ; QFile newLicense(d->availableLicenses[licenseIndex].path); if(newLicense.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream newLicenseText(&newLicense); newLicenseText.setAutoDetectUnicode(true); licenseText = newLicenseText.readAll(); newLicense.close(); } else licenseText = "Error, could not open license file.\n Was it deleted?"; } /* Add date, name and email to license text */ licenseText.replace("", QDate::currentDate().toString("yyyy")); QString developer("%1 <%2>"); KEMailSettings* emailSettings = new KEMailSettings(); QString name = emailSettings->getSetting(KEMailSettings::RealName); if (name.isEmpty()) { name = ""; } developer = developer.arg(name); QString email = emailSettings->getSetting(KEMailSettings::EmailAddress); if (email.isEmpty()) { email = "email"; //no < > as they are already through the email field } developer = developer.arg(email); licenseText.replace("", developer); d->availableLicenses[licenseIndex].contents = licenseText; } return d->availableLicenses[licenseIndex].contents; } // ---Slots--- void LicensePage::licenseComboChanged(int selectedLicense) { //If the last slot is selected enable the save license combobox if(selectedLicense == (d->availableLicenses.size() - 1)) { d->license->licenseTextEdit->clear(); d->license->licenseTextEdit->setReadOnly(false); d->license->saveLicense->setEnabled(true); } else { d->license->saveLicense->setEnabled(false); d->license->licenseTextEdit->setReadOnly(true); } if(selectedLicense < 0 || selectedLicense >= d->availableLicenses.size()) d->license->licenseTextEdit->setText(i18n("Could not load previous license")); else d->license->licenseTextEdit->setText(readLicense(selectedLicense)); } bool LicensePage::saveLicense() { kDebug() << "Attempting to save custom license: " << d->license->licenseName->text(); QString localDataDir = KStandardDirs::locateLocal("data", "kdevcodegen/licenses/", KGlobal::activeComponent()); QFile newFile(localDataDir + d->license->licenseName->text()); if(newFile.exists()) { KMessageBox::sorry(this, i18n("The specified license already exists. Please provide a different name.")); return false; } newFile.open(QIODevice::WriteOnly); qint64 result = newFile.write(d->license->licenseTextEdit->toPlainText().toUtf8()); newFile.close(); if(result == -1) { KMessageBox::sorry(this, i18n("There was an error writing the file.")); return false; } return true; } class OutputPagePrivate { public: OutputPagePrivate() : output(0) { } Ui::OutputLocationDialog* output; CreateClassWizard* parent; void updateRanges(KIntNumInput * line, KIntNumInput * column, bool enable); }; void OutputPagePrivate::updateRanges(KIntNumInput * line, KIntNumInput * column, bool enable) { kDebug() << "Updating Ranges, file exists: " << enable; line->setEnabled(enable); column->setEnabled(enable); } OutputPage::OutputPage(CreateClassWizard* parent) : QWizardPage(parent) , d(new OutputPagePrivate) { d->parent = parent; setTitle(i18n("Output")); setSubTitle( i18n("Choose where to save the new class.") ); d->output = new Ui::OutputLocationDialog; d->output->setupUi(this); d->output->headerUrl->setMode( KFile::File | KFile::LocalOnly ); d->output->headerUrl->fileDialog()->setOperationMode( KFileDialog::Saving ); d->output->implementationUrl->setMode( KFile::File | KFile::LocalOnly ); d->output->implementationUrl->fileDialog()->setOperationMode( KFileDialog::Saving ); connect(d->output->lowerFilenameCheckBox, SIGNAL(stateChanged(int)), this, SLOT(updateFileNames())); connect(d->output->headerUrl, SIGNAL(textChanged(const QString &)), this, SLOT(updateHeaderRanges(const QString &))); connect(d->output->implementationUrl, SIGNAL(textChanged(const QString &)), this, SLOT(updateImplementationRanges(const QString &))); registerField("headerUrl", d->output->headerUrl); registerField("implementationUrl", d->output->implementationUrl); } void OutputPage::initializePage() { + //Read the setting for lower case filenames + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup codegenGroup( config, "CodeGeneration" ); + bool lower = codegenGroup.readEntry( "LowerCaseFilenames", true ); + d->parent->d->baseUrl, d->output->lowerFilenameCheckBox->setChecked(lower); + updateFileNames(); QWizardPage::initializePage(); } void OutputPage::updateFileNames() { d->output->headerUrl->setUrl(d->parent->generator()->headerUrlFromBase(d->parent->d->baseUrl, d->output->lowerFilenameCheckBox->isChecked())); d->output->implementationUrl->setUrl(d->parent->generator()->implementationUrlFromBase(d->parent->d->baseUrl, d->output->lowerFilenameCheckBox->isChecked())); + + //Save the setting for next time + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup codegenGroup( config, "CodeGeneration" ); + codegenGroup.writeEntry( "LowerCaseFilenames", d->output->lowerFilenameCheckBox->isChecked() ); } void OutputPage::updateHeaderRanges(const QString & url) { QFileInfo info(url); d->updateRanges(d->output->headerLineNumber, d->output->headerColumnNumber, info.exists() && !info.isDir()); } void OutputPage::updateImplementationRanges(const QString & url) { QFileInfo info(url); d->updateRanges(d->output->implementationLineNumber, d->output->implementationColumnNumber, info.exists() && !info.isDir()); } bool OutputPage::isComplete() const { return !d->output->headerUrl->url().url().isEmpty() && !d->output->implementationUrl->url().url().isEmpty(); } bool OutputPage::validatePage() { d->parent->generator()->setHeaderUrl(d->output->headerUrl->text()); d->parent->generator()->setImplementationUrl(d->output->implementationUrl->text()); d->parent->generator()->setHeaderPosition(SimpleCursor(d->output->headerLineNumber->value(), d->output->headerColumnNumber->value())); d->parent->generator()->setImplementationPosition(SimpleCursor(d->output->implementationLineNumber->value(), d->output->implementationColumnNumber->value())); return true; } OutputPage::~OutputPage() { delete d; } } #include "createclass.moc" diff --git a/language/codegen/documentchangeset.cpp b/language/codegen/documentchangeset.cpp index 0c8efd2b1d..36e10ac916 100644 --- a/language/codegen/documentchangeset.cpp +++ b/language/codegen/documentchangeset.cpp @@ -1,419 +1,456 @@ /* 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 "documentchangeset.h" #include "coderepresentation.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KDevelop { struct DocumentChangeSetPrivate { DocumentChangeSet::ReplacementPolicy replacePolicy; DocumentChangeSet::FormatPolicy formatPolicy; DocumentChangeSet::DUChainUpdateHandling updatePolicy; DocumentChangeSet::ActivationPolicy activationPolicy; QMap< IndexedString, QList > changes; DocumentChangeSet::ChangeResult addChange(DocumentChangePointer change); DocumentChangeSet::ChangeResult replaceOldText(CodeRepresentation * repr, const QString & newText, const QList & sortedChangesList); DocumentChangeSet::ChangeResult generateNewText(const KDevelop::IndexedString & file, QList< KDevelop::DocumentChangePointer > & sortedChanges, const KDevelop::CodeRepresentation* repr, QString& output); DocumentChangeSet::ChangeResult removeDuplicates(const IndexedString & file, QList & filteredChanges); void formatChanges(); void updateFiles(); }; //Simple helper to clear up code clutter namespace { inline bool changeIsValid(const DocumentChange & change, const QStringList & textLines) { return change.m_range.start <= change.m_range.end && change.m_range.end.line < textLines.size() && change.m_range.start.line >= 0 && change.m_range.start.column >= 0 && change.m_range.start.column <= textLines[change.m_range.start.line].length() && change.m_range.end.column >= 0 && change.m_range.end.column <= textLines[change.m_range.end.line].length() && change.m_range.start.line == change.m_range.end.line; } inline bool duplicateChanges(DocumentChangePointer previous, DocumentChangePointer current) { //Given the option of considering a duplicate two changes in the same range but with different old texts to be ignored return previous->m_range == current->m_range && previous->m_newText == current->m_newText && (previous->m_oldText == current->m_oldText || (previous->m_ignoreOldText && current->m_ignoreOldText)); } } DocumentChangeSet::DocumentChangeSet() : d(new DocumentChangeSetPrivate) { d->replacePolicy = StopOnFailedChange; d->formatPolicy = AutoFormatChanges; d->updatePolicy = SimpleUpdate; d->activationPolicy = DoNotActivate; } DocumentChangeSet::DocumentChangeSet(const DocumentChangeSet & rhs) : d(new DocumentChangeSetPrivate(*rhs.d)) { } DocumentChangeSet& DocumentChangeSet::operator=(const KDevelop::DocumentChangeSet& rhs) { *d = *rhs.d; return *this; } DocumentChangeSet::~DocumentChangeSet() { delete d; } KDevelop::DocumentChangeSet::ChangeResult DocumentChangeSet::addChange(const KDevelop::DocumentChange& change) { return d->addChange(DocumentChangePointer(new DocumentChange(change))); } DocumentChangeSet::ChangeResult DocumentChangeSet::addChange(DocumentChangePointer change) { return d->addChange(change); } DocumentChangeSet::ChangeResult DocumentChangeSetPrivate::addChange(DocumentChangePointer change) { if(change->m_range.start.line != change->m_range.end.line) return DocumentChangeSet::ChangeResult("Multi-line ranges are not supported"); changes[change->m_document].append(change); return true; } void DocumentChangeSet::setReplacementPolicy ( DocumentChangeSet::ReplacementPolicy policy ) { d->replacePolicy = policy; } void DocumentChangeSet::setFormatPolicy ( DocumentChangeSet::FormatPolicy policy ) { d->formatPolicy = policy; } void DocumentChangeSet::setUpdateHandling ( DocumentChangeSet::DUChainUpdateHandling policy ) { d->updatePolicy = policy; } void DocumentChangeSet::setActivationPolicy(DocumentChangeSet::ActivationPolicy policy) { d->activationPolicy = policy; } QMap< IndexedString, InsertArtificialCodeRepresentationPointer > DocumentChangeSet::temporaryCodeRepresentations() const { QMap< IndexedString, InsertArtificialCodeRepresentationPointer > ret; ChangeResult result(true); foreach(const IndexedString &file, d->changes.keys()) { CodeRepresentation::Ptr repr = createCodeRepresentation(file); if(!repr) continue; QList sortedChangesList; result = d->removeDuplicates(file, sortedChangesList); if(!result) continue; QString newText; result = d->generateNewText(file, sortedChangesList, repr.data(), newText); if(!result) continue; InsertArtificialCodeRepresentationPointer code( new InsertArtificialCodeRepresentation(IndexedString(file.toUrl().fileName()), newText) ); ret.insert(file, code); } return ret; } DocumentChangeSet::ChangeResult DocumentChangeSet::applyAllChanges() { QMap codeRepresentations; QMap newTexts; QMap > filteredSortedChanges; ChangeResult result(true); QList files(d->changes.keys()); foreach(const IndexedString &file, files) { CodeRepresentation::Ptr repr = createCodeRepresentation(file); if(!repr) return ChangeResult(QString("Could not create a Representation for %1").arg(file.str())); codeRepresentations[file] = repr; QList& sortedChangesList(filteredSortedChanges[file]); { result = d->removeDuplicates(file, sortedChangesList); if(!result) return result; } { result = d->generateNewText(file, sortedChangesList, repr.data(), newTexts[file]); if(!result) return result; } } QMap oldTexts; //Apply the changes to the files foreach(const IndexedString &file, files) { oldTexts[file] = codeRepresentations[file]->text(); result = d->replaceOldText(codeRepresentations[file].data(), newTexts[file], filteredSortedChanges[file]); if(!result && d->replacePolicy == StopOnFailedChange) { //Revert all files foreach(const IndexedString &revertFile, oldTexts.keys()) codeRepresentations[revertFile]->setText(oldTexts[revertFile]); return result; } } d->updateFiles(); if(d->activationPolicy == Activate) foreach(const IndexedString& file, files) ICore::self()->documentController()->openDocument(file.toUrl()); return result; } DocumentChangeSet::ChangeResult DocumentChangeSetPrivate::replaceOldText(CodeRepresentation * repr, const QString & newText, const QList & sortedChangesList) { DynamicCodeRepresentation* dynamic = dynamic_cast(repr); if(dynamic) { dynamic->startEdit(); //Replay the changes one by one for(int pos = sortedChangesList.size()-1; pos >= 0; --pos) { const DocumentChange& change(*sortedChangesList[pos]); if(!dynamic->replace(change.m_range.textRange(), change.m_oldText, change.m_newText, change.m_ignoreOldText)) { QString warningString = QString("Inconsistent change in %1 at %2:%3 -> %4:%5 = %6(encountered \"%7\") -> \"%8\"") .arg(change.m_document.str()).arg(change.m_range.start.line).arg(change.m_range.start.column) .arg(change.m_range.end.line).arg(change.m_range.end.column).arg(change.m_oldText) .arg(dynamic->rangeText(change.m_range.textRange())).arg(change.m_newText); if(replacePolicy == DocumentChangeSet::WarnOnFailedChange) { kWarning() << warningString; } else if(replacePolicy == DocumentChangeSet::StopOnFailedChange) { dynamic->endEdit(); return DocumentChangeSet::ChangeResult(warningString); } //If set to ignore failed changes just continue with the others } } dynamic->endEdit(); return true; } //For files on disk if (!repr->setText(newText)) { QString warningString = QString("Could not replace text in the document: %1").arg(sortedChangesList.begin()->data()->m_document.str()); if(replacePolicy == DocumentChangeSet::WarnOnFailedChange) { kWarning() << warningString; } return DocumentChangeSet::ChangeResult(warningString); } return true; } DocumentChangeSet::ChangeResult DocumentChangeSetPrivate::generateNewText(const IndexedString & file, QList & sortedChanges, const CodeRepresentation * repr, QString & output) { ISourceFormatter* formatter = 0; if(ICore::self()) formatter = ICore::self()->sourceFormatterController()->formatterForUrl(file.toUrl()); //Create the actual new modified file QStringList textLines = repr->text().split('\n'); KMimeType::Ptr mime = KMimeType::findByUrl(file.toUrl()); for(int pos = sortedChanges.size()-1; pos >= 0; --pos) { DocumentChange& change(*sortedChanges[pos]); QString encountered; if(changeIsValid(change, textLines) && //We demand this, although it should be fixed ((encountered = textLines[change.m_range.start.line].mid(change.m_range.start.column, change.m_range.end.column-change.m_range.start.column)) == change.m_oldText || change.m_ignoreOldText)) { ///Problem: This does not work if the other changes significantly alter the context @todo Use the changed context QString leftContext = QStringList(textLines.mid(0, change.m_range.start.line+1)).join("\n"); leftContext.chop(textLines[change.m_range.start.line].length() - change.m_range.start.column); QString rightContext = QStringList(textLines.mid(change.m_range.end.line)).join("\n").mid(change.m_range.end.column); - if(formatter && formatPolicy == DocumentChangeSet::AutoFormatChanges) + if(formatter && (formatPolicy == DocumentChangeSet::AutoFormatChanges || formatPolicy == DocumentChangeSet::AutoFormatChangesKeepIndentation)) + { + QString oldNewText = change.m_newText; change.m_newText = formatter->formatSource(change.m_newText, mime, leftContext, rightContext); + + if(formatPolicy == DocumentChangeSet::AutoFormatChangesKeepIndentation) + { + // Reproduce the previous indentation + QStringList oldLines = oldNewText.split("\n"); + QStringList newLines = change.m_newText.split("\n"); + + if(oldLines.size() == newLines.size()) + { + for(uint line = 0; line < newLines.size(); ++line) + { + // Keep the previous indentation + QString oldIndentation; + for(uint a = 0; a < oldLines[line].size(); ++a) + if(oldLines[line][a].isSpace()) + oldIndentation.append(oldLines[line][a]); + else + break; + + int newIndentationLength = 0; + + for(int a = 0; a < newLines[line].size(); ++a) + if(change.m_newText[a].isSpace()) + newIndentationLength = a; + else + break; + + newLines[line].replace(0, newIndentationLength, oldIndentation); + } + change.m_newText = newLines.join("\n"); + }else{ + kDebug() << "Cannot keep the indentation because the line count has changed" << oldNewText; + } + } + } textLines[change.m_range.start.line].replace(change.m_range.start.column, change.m_range.end.column-change.m_range.start.column, change.m_newText); }else{ QString warningString = QString("Inconsistent change in %1 at %2:%3 -> %4:%5 = \"%6\"(encountered \"%7\") -> \"%8\"") .arg(file.str()).arg(change.m_range.start.line).arg(change.m_range.start.column) .arg(change.m_range.end.line).arg(change.m_range.end.column).arg(change.m_oldText) .arg(encountered).arg(change.m_newText); if(replacePolicy == DocumentChangeSet::IgnoreFailedChange) { //Just don't do the replacement }else if(replacePolicy == DocumentChangeSet::WarnOnFailedChange) kWarning() << warningString; else return DocumentChangeSet::ChangeResult(warningString, sortedChanges[pos]); } } output = textLines.join("\n"); return true; } //Removes all duplicate changes for a single file, and then returns (via filteredChanges) the filtered duplicates DocumentChangeSet::ChangeResult DocumentChangeSetPrivate::removeDuplicates(const IndexedString & file, QList & filteredChanges) { QMultiMap sortedChanges; foreach(const DocumentChangePointer &change, changes[file]) sortedChanges.insert(change->m_range.end, change); //Remove duplicates QMultiMap::iterator previous = sortedChanges.begin(); for(QMultiMap::iterator it = ++sortedChanges.begin(); it != sortedChanges.end(); ) { if(( *previous ) && ( *previous )->m_range.end > (*it)->m_range.start) { //intersection if(duplicateChanges(( *previous ), *it)) { //duplicate, remove one it = sortedChanges.erase(it); continue; } //When two changes contain each other, and the container change is set to ignore old text, then it should be safe to //just ignore the contained change, and apply the bigger change else if((*it)->m_range.contains(( *previous )->m_range) && (*it)->m_ignoreOldText ) { kDebug() << "Removing change: " << ( *previous )->m_oldText << "->" << ( *previous )->m_newText << ", because it is contained by change: " << (*it)->m_oldText << "->" << (*it)->m_newText; sortedChanges.erase(previous); } //This case is for when both have the same end, either of them could be the containing range else if((*previous)->m_range.contains((*it)->m_range) && (*previous)->m_ignoreOldText ) { kDebug() << "Removing change: " << (*it)->m_oldText << "->" << (*it)->m_newText << ", because it is contained by change: " << ( *previous )->m_oldText << "->" << ( *previous )->m_newText; it = sortedChanges.erase(it); continue; } else return DocumentChangeSet::ChangeResult( QString("Inconsistent change-request at %1; intersecting changes: \"%2\"->\"%3\"@%4:%5->%6:%7 & \"%8\"->\"%9\"@%10:%11->%12:%13 ") .arg(file.str(), ( *previous )->m_oldText, ( *previous )->m_newText).arg(( *previous )->m_range.start.line).arg(( *previous )->m_range.start.column) .arg(( *previous )->m_range.end.line).arg(( *previous )->m_range.end.column).arg((*it)->m_oldText, (*it)->m_newText).arg((*it)->m_range.start.line) .arg((*it)->m_range.start.column).arg((*it)->m_range.end.line).arg((*it)->m_range.end.column)); } previous = it; ++it; } filteredChanges = sortedChanges.values(); return true; } void DocumentChangeSetPrivate::updateFiles() { ModificationRevisionSet::clearCache(); foreach(const IndexedString& file, changes.keys()) ModificationRevision::clearModificationCache(file); if(updatePolicy != DocumentChangeSet::NoUpdate && ICore::self()) { // The active document should be updated first, so that the user sees the results instantly if(ICore::self()->documentController()->activeDocument()) ICore::self()->languageController()->backgroundParser()->addDocument(ICore::self()->documentController()->activeDocument()->url()); // If there are currently open documents that now need an update, update them too foreach(const IndexedString& doc, ICore::self()->languageController()->backgroundParser()->managedDocuments()) { DUChainReadLocker lock(DUChain::lock()); TopDUContext* top = DUChainUtils::standardContextForUrl(doc.toUrl(), true); if((top && top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->needsUpdate()) || !top) { lock.unlock(); ICore::self()->languageController()->backgroundParser()->addDocument(doc.toUrl()); } } // Eventually update _all_ affected files foreach(const IndexedString &file, changes.keys()) { if(!file.toUrl().isValid()) { kWarning() << "Trying to apply changes to an invalid document"; continue; } ICore::self()->languageController()->backgroundParser()->addDocument(file.toUrl()); } } } } diff --git a/language/codegen/documentchangeset.h b/language/codegen/documentchangeset.h index c724879cc6..cca44745dc 100644 --- a/language/codegen/documentchangeset.h +++ b/language/codegen/documentchangeset.h @@ -1,134 +1,135 @@ /* 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 DOCUMENTCHANGESET_H #define DOCUMENTCHANGESET_H #include #include #include #include #include "coderepresentation.h" namespace KDevelop { struct DocumentChangeSetPrivate; class KDEVPLATFORMLANGUAGE_EXPORT DocumentChange : public QSharedData { public: DocumentChange(const IndexedString& document, const SimpleRange& range, const QString& oldText, const QString& newText) : m_document(document), m_range(range), m_oldText(oldText), m_newText(newText), m_ignoreOldText(false) { //Clean the URL, so we don't get the same file be stored as a different one KUrl url(m_document.toUrl()); url.cleanPath(); m_document = IndexedString(url); } IndexedString m_document; SimpleRange m_range; QString m_oldText; QString m_newText; bool m_ignoreOldText; //Set this to disable the verification of m_oldText. This can be used to overwrite arbitrary text, but is dangerous! }; typedef KSharedPtr DocumentChangePointer; /** * Object representing an arbitrary set of changes to an arbitrary set of files that can be applied atomically. */ class KDEVPLATFORMLANGUAGE_EXPORT DocumentChangeSet { public: DocumentChangeSet(); ~DocumentChangeSet(); DocumentChangeSet(const DocumentChangeSet & rhs); DocumentChangeSet& operator=(const DocumentChangeSet& rhs); //Returns true on success struct ChangeResult { ChangeResult(bool success) : m_success(success) { } ChangeResult(QString failureReason, DocumentChangePointer reasonChange = DocumentChangePointer()) : m_failureReason(failureReason), m_reasonChange(reasonChange), m_success(false) { } operator bool() const { return m_success; } /// Reason why the change failed QString m_failureReason; /// Specific change that caused the problem (might be 0) DocumentChangePointer m_reasonChange; bool m_success; }; ///If the change has multiple lines, a problem will be returned. these don't work at he moment. ChangeResult addChange(const DocumentChange& change); ChangeResult addChange(DocumentChangePointer change); enum ReplacementPolicy { IgnoreFailedChange,///If this is given, all changes that could not be applied are simply ignored WarnOnFailedChange,///If this is given to applyAllChanges, a warning is given when a change could not be applied, ///but following changes are applied, and success is returned. StopOnFailedChange ///If this is given to applyAllChanges, then all replacements are reverted and an error returned on problems (default) }; ///@param policy What should be done when a change could not be applied? void setReplacementPolicy(ReplacementPolicy policy); enum FormatPolicy { NoAutoFormat, ///If this option is given, no automatic formatting is applied - AutoFormatChanges ///If this option is given, all changes are automatically reformatted using the formatter plugin for the mime type (default) + AutoFormatChanges, ///If this option is given, all changes are automatically reformatted using the formatter plugin for the mime type (default) + AutoFormatChangesKeepIndentation ///Same as AutoFormatChanges, except that the indentation of inserted lines is kept equal }; ///@param policy How the changed text should be formatted void setFormatPolicy(FormatPolicy policy); enum DUChainUpdateHandling { NoUpdate, ///No updates will be scheduled SimpleUpdate ///The changed documents will be added to the background parser, plus all documents that are currently open and recursively import those documents (default) //FullUpdate ///All documents in all open projects that recursively import any of the changed documents will be updated }; ///@param policy Whether a duchain update should be triggered for all affected documents void setUpdateHandling(DUChainUpdateHandling policy); enum ActivationPolicy { Activate, ///The affected files will be activated DoNotActivate ///The affected files will not be activated (default) }; ///@param policy Whether the affected documents should be activated when the change is applied void setActivationPolicy(ActivationPolicy policy); ///Applies all changes to temporary code representations, and returns a map from each file-name to ///the respective inserted artificial code-representation. QMap temporaryCodeRepresentations() const; /// Apply all the changes registered in this changeset to the actual files ChangeResult applyAllChanges(); private: DocumentChangeSetPrivate * d; }; } #endif diff --git a/language/duchain/topducontextdynamicdata.cpp b/language/duchain/topducontextdynamicdata.cpp index 99a1570cfd..c9cd97544d 100644 --- a/language/duchain/topducontextdynamicdata.cpp +++ b/language/duchain/topducontextdynamicdata.cpp @@ -1,786 +1,785 @@ /* This is part of KDevelop 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 "topducontextdynamicdata.h" #include #include #include #include #include "declaration.h" #include "declarationdata.h" #include "ducontext.h" #include "topducontext.h" #include "topducontextdata.h" #include "ducontextdata.h" #include "duchainregister.h" #include "repositories/itemrepository.h" //#define DEBUG_DATA_INFO //This might be problematic on some systems, because really many mmaps are created #define USE_MMAP using namespace KDevelop; QMutex KDevelop::TopDUContextDynamicData::m_temporaryDataMutex(QMutex::Recursive); void saveDUChainItem(QList& data, DUChainBase& item, uint& totalDataOffset) { if(!item.d_func()->classId) { //If this triggers, you have probably created an own DUChainBase based class, but haven't called setClassId(this) in the constructor. kError() << QString("no class-id set for data attached to a declaration of type %1").arg(typeid(item).name()); Q_ASSERT(0); } int size = DUChainItemSystem::self().dynamicSize(*item.d_func()); if(data.back().first.size() - int(data.back().second) < size) //Create a new data item data.append( qMakePair(QByteArray(size > 10000 ? size : 10000, 0), 0u) ); uint pos = data.back().second; data.back().second += size; totalDataOffset += size; DUChainBaseData& target(*((DUChainBaseData*)(data.back().first.constData() + pos))); if(item.d_func()->isDynamic()) { //Change from dynamic data to constant data enableDUChainReferenceCounting(data.back().first.data(), data.back().first.size()); DUChainItemSystem::self().copy(*item.d_func(), target, true); Q_ASSERT(!target.isDynamic()); item.setData(&target); disableDUChainReferenceCounting(data.back().first.data()); }else{ //Just copy the data into another place, expensive copy constructors are not needed memcpy(&target, item.d_func(), size); item.setData(&target, false); } Q_ASSERT(item.d_func() == &target); Q_ASSERT(!item.d_func()->isDynamic()); } const char* KDevelop::TopDUContextDynamicData::pointerInData(const QList& data, uint totalOffset) { for(int a = 0; a < data.size(); ++a) { if(totalOffset < data[a].second) return data[a].first.constData() + totalOffset; totalOffset -= data[a].second; } Q_ASSERT(0); //Offset doesn't exist in the data return 0; } const char* KDevelop::TopDUContextDynamicData::pointerInData(uint totalOffset) const { Q_ASSERT(!m_mappedData || m_data.isEmpty()); if(m_mappedData && m_mappedDataSize) return (char*)m_mappedData + totalOffset; for(int a = 0; a < m_data.size(); ++a) { if(totalOffset < m_data[a].second) return m_data[a].first.constData() + totalOffset; totalOffset -= m_data[a].second; } Q_ASSERT(0); //Offset doesn't exist in the data return 0; } void TopDUContextDynamicData::verifyDataInfo(const ItemDataInfo& info, const QList& data) { Q_UNUSED(info); Q_UNUSED(data); #ifdef DEBUG_DATA_INFO DUChainBaseData* item = (DUChainBaseData*)(pointerInData(data, info.dataOffset)); int size = DUChainItemSystem::self().dynamicSize(*item); Q_ASSERT(size); #endif } TopDUContextDynamicData::ItemDataInfo TopDUContextDynamicData::writeDataInfo(const ItemDataInfo& info, const DUChainBaseData* data, uint& totalDataOffset) { ItemDataInfo ret(info); Q_ASSERT(info.dataOffset); int size = DUChainItemSystem::self().dynamicSize(*data); Q_ASSERT(size); if(m_data.back().first.size() - int(m_data.back().second) < size) //Create a new m_data item m_data.append( qMakePair(QByteArray(size > 10000 ? size : 10000, 0), 0u) ); ret.dataOffset = totalDataOffset; uint pos = m_data.back().second; m_data.back().second += size; totalDataOffset += size; DUChainBaseData& target(*((DUChainBaseData*)(m_data.back().first.constData() + pos))); memcpy(&target, data, size); verifyDataInfo(ret, m_data); return ret; } TopDUContextDynamicData::TopDUContextDynamicData(TopDUContext* topContext) : m_deleting(false), m_topContext(topContext), m_fastContexts(0), m_fastContextsSize(0), m_fastDeclarations(0), m_fastDeclarationsSize(0), m_onDisk(false), m_dataLoaded(true), m_mappedFile(0), m_mappedData(0), m_mappedDataSize(0), m_itemRetrievalForbidden(false) { } void KDevelop::TopDUContextDynamicData::clearContextsAndDeclarations() { //Due to template specialization it's possible that a declaration is not reachable through the normal context structure. //For that reason we have to check here, and delete all remaining declarations. for(int a = 0; a < m_temporaryContexts.size(); ++a) delete m_temporaryContexts[a]; for(int a = 0; a < m_temporaryDeclarations.size(); ++a) delete m_temporaryDeclarations[a]; for(int a = 0; a < m_contexts.size(); ++a) delete m_contexts[a]; for(int a = 0; a < m_declarations.size(); ++a) delete m_declarations[a]; } TopDUContextDynamicData::~TopDUContextDynamicData() { clearContextsAndDeclarations(); unmap(); } void KDevelop::TopDUContextDynamicData::unmap() { delete m_mappedFile; m_mappedFile = 0; m_mappedData = 0; m_mappedDataSize = 0; } QList TopDUContextDynamicData::loadImporters(uint topContextIndex) { QList ret; QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; QString fileName = baseDir + '/' + QString("%1").arg(topContextIndex); QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { uint readValue; file.read((char*)&readValue, sizeof(uint)); //now readValue is filled with the top-context data size //We only read the most needed stuff, not the whole top-context data QByteArray data = file.read(readValue); const TopDUContextData* topData = (const TopDUContextData*)data.constData(); FOREACH_FUNCTION(const IndexedDUContext& importer, topData->m_importers) ret << importer; } return ret; } QList TopDUContextDynamicData::loadImports(uint topContextIndex) { QList ret; QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; QString fileName = baseDir + '/' + QString("%1").arg(topContextIndex); QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { uint readValue; file.read((char*)&readValue, sizeof(uint)); //now readValue is filled with the top-context data size //We only read the most needed stuff, not the whole top-context data QByteArray data = file.read(readValue); const TopDUContextData* topData = (const TopDUContextData*)data.constData(); FOREACH_FUNCTION(const DUContext::Import& import, topData->m_importedContexts) ret << import.indexedContext(); } return ret; } bool TopDUContextDynamicData::fileExists(uint topContextIndex) { QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; QString fileName = baseDir + '/' + QString("%1").arg(topContextIndex); QFile file(fileName); return file.exists(); } IndexedString TopDUContextDynamicData::loadUrl(uint topContextIndex) { QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; QString fileName = baseDir + '/' + QString("%1").arg(topContextIndex); QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { uint readValue; file.read((char*)&readValue, sizeof(uint)); //now readValue is filled with the top-context data size //We only read the most needed stuff, not the whole top-context data QByteArray data = file.read(sizeof(TopDUContextData)); const TopDUContextData* topData = (const TopDUContextData*)data.constData(); Q_ASSERT(topData->m_url.isEmpty() || topData->m_url.index() >> 16); return topData->m_url; } return IndexedString(); } void TopDUContextDynamicData::loadData() const { //This function has to be protected by an additional mutex, since it can be triggered from multiple threads at the same time static QMutex mutex; QMutexLocker lock(&mutex); if(m_dataLoaded) return; Q_ASSERT(!m_dataLoaded); Q_ASSERT(m_data.isEmpty()); Q_ASSERT(m_contextDataOffsets.isEmpty()); Q_ASSERT(m_declarations.isEmpty()); QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; KStandardDirs::makeDir(baseDir); QString fileName = baseDir + '/' + QString("%1").arg(m_topContext->ownIndex()); QFile* file = new QFile(fileName); bool open = file->open(QIODevice::ReadOnly); Q_ASSERT(open); Q_ASSERT(file->size()); //Skip the offsets, we're already read them //Skip top-context data uint readValue; file->read((char*)&readValue, sizeof(uint)); file->seek(readValue + file->pos()); file->read((char*)&readValue, sizeof(uint)); m_contextDataOffsets.resize(readValue); file->read((char*)m_contextDataOffsets.data(), sizeof(ItemDataInfo) * m_contextDataOffsets.size()); file->read((char*)&readValue, sizeof(uint)); m_declarationDataOffsets.resize(readValue); file->read((char*)m_declarationDataOffsets.data(), sizeof(ItemDataInfo) * m_declarationDataOffsets.size()); #ifdef USE_MMAP m_mappedData = file->map(file->pos(), file->size() - file->pos()); if(m_mappedData) { m_mappedFile = file; m_mappedDataSize = file->size() - file->pos(); file->close(); //Close the file, so there is less open file descriptors(May be problematic) }else{ kDebug() << "Failed to map" << fileName; } #endif if(!m_mappedFile) { QByteArray data = file->readAll(); m_data.append(qMakePair(data, (uint)data.size())); delete file; } //Fill with zeroes for now, will be initialized on-demand m_contexts.resize(m_contextDataOffsets.size()); m_fastContexts = m_contexts.data(); m_fastContextsSize = m_contexts.size(); m_declarations.resize(m_declarationDataOffsets.size()); m_fastDeclarations = m_declarations.data(); m_fastDeclarationsSize = m_declarations.size(); m_dataLoaded = true; } TopDUContext* TopDUContextDynamicData::load(uint topContextIndex) { QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; KStandardDirs::makeDir(baseDir); QString fileName = baseDir + '/' + QString("%1").arg(topContextIndex); QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { if(file.size() == 0) { kWarning() << "Top-context file is empty" << fileName; return 0; } QVector contextDataOffsets; QVector declarationDataOffsets; uint readValue; file.read((char*)&readValue, sizeof(uint)); //now readValue is filled with the top-context data size QByteArray topContextData = file.read(readValue); DUChainBaseData* topData = (DUChainBaseData*)topContextData.constData(); /* IndexedString language = static_cast(topData)->m_language; if(!language.isEmpty()) {*/ ///@todo Load the language if it isn't loaded yet, problem: We're possibly not in the foreground thread! // } TopDUContext* ret = dynamic_cast(DUChainItemSystem::self().create(topData)); if(!ret) { kWarning() << "Cannot load a top-context, the requered language-support is probably not loaded"; return 0; } //Disable the updating flag on loading. Sometimes it may be set when the context was saved while it was updated ret->setFlags( (TopDUContext::Flags) (ret->flags() & (~TopDUContext::UpdatingContext)) ); TopDUContextDynamicData& target(*ret->m_dynamicData); // kDebug() << "loaded" << ret->url().str() << ret->ownIndex() << "import-count:" << ret->importedParentContexts().size() << ret->d_func()->m_importedContextsSize(); target.m_data.clear(); target.m_dataLoaded = false; target.m_onDisk = true; ret->rebuildDynamicData(0, topContextIndex); target.m_topContextData.append(qMakePair(topContextData, (uint)0)); // kDebug() << "loaded" << ret->url().str() << ret->ownIndex() << "import-count:" << ret->importedParentContexts().size() << ret->d_func()->m_importedContextsSize(); return ret; }else{ - kWarning() << "Cannot open top-context for reading:" << fileName; return 0; } } bool TopDUContextDynamicData::isOnDisk() const { return m_onDisk; } void TopDUContextDynamicData::deleteOnDisk() { if(!isOnDisk()) return; kDebug() << "deleting" << m_topContext->ownIndex() << m_topContext->url().str(); if(!m_dataLoaded) loadData(); for(int a = 0; a < m_fastContextsSize; ++a) if(m_fastContexts[a]) m_fastContexts[a]->makeDynamic(); for(int a = 0; a < m_fastDeclarationsSize; ++a) if(m_fastDeclarations[a]) m_fastDeclarations[a]->makeDynamic(); m_topContext->makeDynamic(); m_onDisk = false; bool successfullyRemoved = QFile::remove(filePath()); Q_ASSERT(successfullyRemoved); kDebug() << "deletion ready"; } QString KDevelop::TopDUContextDynamicData::filePath() const { QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; KStandardDirs::makeDir(baseDir); return baseDir + '/' + QString("%1").arg(m_topContext->ownIndex()); } void TopDUContextDynamicData::store() { // kDebug() << "storing" << m_topContext->url().str() << m_topContext->ownIndex() << "import-count:" << m_topContext->importedParentContexts().size(); bool contentDataChanged = false; if(m_onDisk) { //Check if something has changed. If nothing has changed, don't store to disk. bool someThingChanged = false; if(m_topContext->d_ptr->m_dynamic) someThingChanged = true; for(int a = 0; a < m_fastContextsSize; ++a) { if(m_fastContexts[a] && m_fastContexts[a]->d_ptr->m_dynamic) { someThingChanged = true; contentDataChanged = true; } if(contentDataChanged) break; } for(int a = 0; a < m_fastDeclarationsSize; ++a) { if(m_fastDeclarations[a] && m_fastDeclarations[a]->d_ptr->m_dynamic) { someThingChanged = true; contentDataChanged = true; } if(contentDataChanged) break; } if(!someThingChanged) return; }else{ contentDataChanged = true; } ///@todo Save the meta-data into a repository, and only the actual content data into a file. /// This will make saving+loading more efficient, and will reduce the disk-usage. /// Then we also won't need to load the data if only the meta-data changed. if(!m_dataLoaded) loadData(); ///If the data is mapped, and we re-write the file, we must make sure that the data is copied out of the map, ///even if only metadata is changed. ///@todo If we split up data and metadata, we don't need to do this if(m_mappedData) contentDataChanged = true; m_topContext->makeDynamic(); m_topContextData.clear(); Q_ASSERT(m_topContext->d_func()->m_ownIndex == m_topContext->ownIndex()); uint topContextDataSize = DUChainItemSystem::self().dynamicSize(*m_topContext->d_func()); m_topContextData.append( qMakePair(QByteArray(DUChainItemSystem::self().dynamicSize(*m_topContext->d_func()), topContextDataSize), (uint)0) ); uint actualTopContextDataSize = 0; if(contentDataChanged) { //We don't need these structures any more, since we have loaded all the declarations/contexts, and m_data //will be reset which these structures pointed into //Load all lazy declarations/contexts QList oldData = m_data; //Keep the old data alive until everything is stored into a new data structure m_data.clear(); uint newDataSize = 0; foreach(const ArrayWithPosition &array, oldData) newDataSize += array.second; if(newDataSize < 10000) newDataSize = 10000; //We always put 1 byte to the front, so we don't have zero data-offsets, since those are used for "invalid". uint currentDataOffset = 1; m_data.append( qMakePair(QByteArray(newDataSize, 0), currentDataOffset) ); QVector oldContextDataOffsets = m_contextDataOffsets; QVector oldDeclarationDataOffsets = m_declarationDataOffsets; m_contextDataOffsets.clear(); m_declarationDataOffsets.clear(); m_itemRetrievalForbidden = true; for(int a = 0; a < m_fastContextsSize; ++a) { if(!m_fastContexts[a]) { if(oldContextDataOffsets.size() > a && oldContextDataOffsets[a].dataOffset) { //Directly copy the old data range into the new data if(m_mappedData) m_contextDataOffsets << writeDataInfo(oldContextDataOffsets[a], (const DUChainBaseData*)(m_mappedData + oldContextDataOffsets[a].dataOffset), currentDataOffset); else m_contextDataOffsets << writeDataInfo(oldContextDataOffsets[a], (const DUChainBaseData*)pointerInData(oldData, oldContextDataOffsets[a].dataOffset), currentDataOffset); }else{ m_contextDataOffsets << ItemDataInfo(); } } else { m_contextDataOffsets << ItemDataInfo(currentDataOffset, LocalIndexedDUContext(m_fastContexts[a]->parentContext()).localIndex()); saveDUChainItem(m_data, *m_fastContexts[a], currentDataOffset); verifyDataInfo(m_contextDataOffsets.back(), m_data); Q_ASSERT(!m_fastContexts[a]->d_func()->isDynamic()); if(m_mappedData) { Q_ASSERT(((size_t)m_fastContexts[a]->d_func()) < ((size_t)m_mappedData) || ((size_t)m_fastContexts[a]->d_func()) > ((size_t)m_mappedData) + m_mappedDataSize); } } } for(int a = 0; a < m_fastDeclarationsSize; ++a) { if(!m_fastDeclarations[a]) { if(oldDeclarationDataOffsets.size() > a && oldDeclarationDataOffsets[a].dataOffset) { //Directly copy the old data range into the new data if(m_mappedData) m_declarationDataOffsets << writeDataInfo(oldDeclarationDataOffsets[a], (const DUChainBaseData*)(m_mappedData + oldDeclarationDataOffsets[a].dataOffset), currentDataOffset); else m_declarationDataOffsets << writeDataInfo(oldDeclarationDataOffsets[a], (const DUChainBaseData*)pointerInData(oldData, oldDeclarationDataOffsets[a].dataOffset), currentDataOffset); }else{ m_declarationDataOffsets << ItemDataInfo(); } } else { m_declarationDataOffsets << ItemDataInfo(currentDataOffset, LocalIndexedDUContext(m_fastDeclarations[a]->context()).localIndex()); saveDUChainItem(m_data, *m_fastDeclarations[a], currentDataOffset); verifyDataInfo(m_declarationDataOffsets.back(), m_data); Q_ASSERT(!m_fastDeclarations[a]->d_func()->isDynamic()); if(m_mappedData) { Q_ASSERT(((size_t)m_fastDeclarations[a]->d_func()) < ((size_t)m_mappedData) || ((size_t)m_fastDeclarations[a]->d_func()) > ((size_t)m_mappedData) + m_mappedDataSize); } } } m_itemRetrievalForbidden = false; for(int a = 0; a < m_fastContextsSize; ++a) if(m_fastContexts[a]) { Q_ASSERT(!m_fastContexts[a]->d_func()->isDynamic()); if(m_mappedData) { Q_ASSERT(((size_t)m_fastContexts[a]->d_func()) < ((size_t)m_mappedData) || ((size_t)m_fastContexts[a]->d_func()) > ((size_t)m_mappedData) + m_mappedDataSize); } } for(int a = 0; a < m_fastDeclarationsSize; ++a) if(m_fastDeclarations[a]) { Q_ASSERT(!m_fastDeclarations[a]->d_func()->isDynamic()); if(m_mappedData) { Q_ASSERT(((size_t)m_fastDeclarations[a]->d_func()) < ((size_t)m_mappedData) || ((size_t)m_fastDeclarations[a]->d_func()) > ((size_t)m_mappedData) + m_mappedDataSize); } } } saveDUChainItem(m_topContextData, *m_topContext, actualTopContextDataSize); Q_ASSERT(actualTopContextDataSize == topContextDataSize); Q_ASSERT(m_topContextData.size() == 1); Q_ASSERT(!m_topContext->d_func()->isDynamic()); unmap(); QFile file(filePath()); if(file.open(QIODevice::WriteOnly)) { file.resize(0); file.write((char*)&topContextDataSize, sizeof(uint)); foreach(const ArrayWithPosition& pos, m_topContextData) file.write(pos.first.constData(), pos.second); uint writeValue = m_contextDataOffsets.size(); file.write((char*)&writeValue, sizeof(uint)); file.write((char*)m_contextDataOffsets.data(), sizeof(ItemDataInfo) * m_contextDataOffsets.size()); writeValue = m_declarationDataOffsets.size(); file.write((char*)&writeValue, sizeof(uint)); file.write((char*)m_declarationDataOffsets.data(), sizeof(ItemDataInfo) * m_declarationDataOffsets.size()); foreach(const ArrayWithPosition& pos, m_data) file.write(pos.first.constData(), pos.second); m_onDisk = true; if (file.size() == 0) { kWarning() << "Saving zero size top ducontext data"; } file.close(); } else { kWarning() << "Cannot open top-context for writing"; } // kDebug() << "stored" << m_topContext->url().str() << m_topContext->ownIndex() << "import-count:" << m_topContext->importedParentContexts().size(); } uint TopDUContextDynamicData::allocateDeclarationIndex(Declaration* decl, bool temporary) { if(!m_dataLoaded) loadData(); if(!temporary) { m_declarations.append(decl); m_fastDeclarations = m_declarations.data(); m_fastDeclarationsSize = m_declarations.size(); return m_fastDeclarationsSize; }else{ QMutexLocker lock(&m_temporaryDataMutex); m_temporaryDeclarations.append(decl); return 0x0fffffff - m_temporaryDeclarations.size(); //We always keep the highest bit at zero } } bool TopDUContextDynamicData::isDeclarationForIndexLoaded(uint index) const { if(!m_dataLoaded) return false; if(index < (0x0fffffff/2)) { if(index == 0 || index > uint(m_fastDeclarationsSize)) return false; return (bool)m_fastDeclarations[index-1]; }else{ return true; } } bool TopDUContextDynamicData::isContextForIndexLoaded(uint index) const { if(!m_dataLoaded) return false; if(index < (0x0fffffff/2)) { if(index == 0) return true; if(index > uint(m_fastContextsSize)) return false; return (bool)m_fastContexts[index-1]; }else{ return true; } } Declaration* TopDUContextDynamicData::getDeclarationForIndex(uint index) const { if(!m_dataLoaded) loadData(); if(index < (0x0fffffff/2)) { if(index == 0 || index > uint(m_fastDeclarationsSize)) { kWarning() << "declaration index out of bounds:" << index << "count:" << m_fastDeclarationsSize; return 0; } else { uint realIndex = index-1; if(!m_fastDeclarations[realIndex] && realIndex < (uint)m_declarationDataOffsets.size() && m_declarationDataOffsets[realIndex].dataOffset) { Q_ASSERT(!m_itemRetrievalForbidden); m_fastDeclarations[realIndex] = static_cast(DUChainItemSystem::self().create((DUChainBaseData*)(pointerInData(m_declarationDataOffsets[realIndex].dataOffset)))); if(!m_fastDeclarations[realIndex]) { //When this happens, the declaration has not been registered correctly. //We can stop here, because else we will get crashes later. kError() << "Failed to load declaration with identity" << ((DUChainBaseData*)(pointerInData(m_declarationDataOffsets[realIndex].dataOffset)))->classId; Q_ASSERT(0); }else{ DUContext* context = getContextForIndex(m_declarationDataOffsets[realIndex].parentContext); Q_ASSERT(context); //If this triggers, the context has been deleted without deleting its contained declarations m_fastDeclarations[realIndex]->rebuildDynamicData(context, index); } } return m_fastDeclarations[realIndex]; } }else{ QMutexLocker lock(&m_temporaryDataMutex); index = 0x0fffffff - index; //We always keep the highest bit at zero if(index == 0 || index > uint(m_temporaryDeclarations.size())) return 0; else return m_temporaryDeclarations[index-1]; } } void TopDUContextDynamicData::clearDeclarationIndex(Declaration* decl) { if(!m_dataLoaded) loadData(); uint index = decl->m_indexInTopContext; if(index < (0x0fffffff/2)) { if(index == 0 || index > uint(m_fastDeclarationsSize)) return; else { Q_ASSERT(m_fastDeclarations[index-1] == decl); m_fastDeclarations[index-1] = 0; if(index-1 < (uint)m_declarationDataOffsets.size()) m_declarationDataOffsets[index-1] = ItemDataInfo(); } }else{ QMutexLocker lock(&m_temporaryDataMutex); index = 0x0fffffff - index; //We always keep the highest bit at zero if(index == 0 || index > uint(m_temporaryDeclarations.size())) return; else { Q_ASSERT(m_temporaryDeclarations[index-1] == decl); m_temporaryDeclarations[index-1] = 0; } } } uint TopDUContextDynamicData::allocateContextIndex(DUContext* decl, bool temporary) { if(!m_dataLoaded) loadData(); if(!temporary) { m_contexts.append(decl); m_fastContexts = m_contexts.data(); m_fastContextsSize = m_contexts.size(); return m_fastContextsSize; }else{ QMutexLocker lock(&m_temporaryDataMutex); m_temporaryContexts.append(decl); return 0x0fffffff - m_temporaryContexts.size(); //We always keep the highest bit at zero } } bool TopDUContextDynamicData::isTemporaryContextIndex(uint index) const { return !(index < (0x0fffffff/2)); } bool TopDUContextDynamicData::isTemporaryDeclarationIndex(uint index) const { return !(index < (0x0fffffff/2)); } DUContext* TopDUContextDynamicData::getContextForIndex(uint index) const { if(!m_dataLoaded) loadData(); if(index < (0x0fffffff/2)) { if(index == 0) return m_topContext; if(index > uint(m_fastContextsSize)) { kWarning() << "declaration index out of bounds:" << index << "count:" << m_fastDeclarationsSize; return 0; } else { uint realIndex = index-1; DUContext** fastContextsPos = (m_fastContexts + realIndex); if(*fastContextsPos) //Shortcut, because this is the most common case return *fastContextsPos; if(!*fastContextsPos && realIndex < (uint)m_contextDataOffsets.size() && m_contextDataOffsets[realIndex].dataOffset) { Q_ASSERT(!m_itemRetrievalForbidden); //Construct the context, and eventuall its parent first *fastContextsPos = dynamic_cast(DUChainItemSystem::self().create((DUChainBaseData*)(pointerInData(m_contextDataOffsets[realIndex].dataOffset)))); if(!*fastContextsPos) { //When this happens, the declaration has not been registered correctly. //We can stop here, because else we will get crashes later. kError() << "Failed to load declaration with identity" << ((DUChainBaseData*)(pointerInData(m_contextDataOffsets[realIndex].dataOffset)))->classId; }else{ (*fastContextsPos)->rebuildDynamicData(getContextForIndex(m_contextDataOffsets[realIndex].parentContext), index); } } return *fastContextsPos; } }else{ QMutexLocker lock(&m_temporaryDataMutex); index = 0x0fffffff - index; //We always keep the highest bit at zero if(index == 0 || index > uint(m_temporaryContexts.size())) return 0; else return m_temporaryContexts[index-1]; } } void TopDUContextDynamicData::clearContextIndex(DUContext* decl) { if(!m_dataLoaded) loadData(); uint index = decl->m_dynamicData->m_indexInTopContext; if(index < (0x0fffffff/2)) { if(index == 0 || index > uint(m_fastContextsSize)) return; else { Q_ASSERT(m_fastContexts[index-1] == decl); m_fastContexts[index-1] = 0; if(index-1 < (uint)m_contextDataOffsets.size()) m_contextDataOffsets[index-1] = ItemDataInfo(); } }else{ QMutexLocker lock(&m_temporaryDataMutex); index = 0x0fffffff - index; //We always keep the highest bit at zero if(index == 0 || index > uint(m_temporaryContexts.size())) return; else { Q_ASSERT(m_temporaryContexts[index-1] == decl); m_temporaryContexts[index-1] = 0; } } } diff --git a/plugins/contextbrowser/kdevcontextbrowser.desktop b/plugins/contextbrowser/kdevcontextbrowser.desktop index a1813d46cf..6796cdb8b9 100644 --- a/plugins/contextbrowser/kdevcontextbrowser.desktop +++ b/plugins/contextbrowser/kdevcontextbrowser.desktop @@ -1,96 +1,96 @@ [Desktop Entry] Type=Service Icon=code-context Exec=blubb Comment=This plugin shows information about the current language context in a side view, and highlights relevant declarations and uses. Comment[bg]=Този приставка показва информация за текущата езикова настройка погледната отвън и маркира съответните декларации и употреби. Comment[ca]=Aquest connector mostra informació quant al context del llenguatge actual en una vista lateral i ressalta les declaracions i els usos apropiats. Comment[ca@valencia]=Este connector mostra informació quant al context del llenguatge actual en una vista lateral i ressalta les declaracions i els usos apropiats. Comment[da]=Dette plugin viser information om den sprogkontekst i et sidepanel og fremhæver relevante erklæringer og brug. Comment[de]=Dieses Modul zeigt Informationen über den aktuellen Sprachkontext an und hebt die wichtigen Deklarationen und Vorkommen hervor. Comment[el]=Αυτό το πρόσθετο εμφανίζει πληροφορίες σχετικά με την τρέχουσα γλώσσα σε μια πλευρική προβολή, και τονίζει τις σχετικές δηλώσεις και χρήσεις. Comment[en_GB]=This plugin shows information about the current language context in a side view, and highlights relevant declarations and uses. Comment[es]=Este complemento muestra información sobre el contexto del lenguaje actual en una vista lateral, resaltando declaraciones relevantes y usos. Comment[et]=See plugin pakub külgvaates teavet aktiivse keele konteksti kohta ning tõstab esile asjakohased deklaratsioonid ja kasutused. Comment[fr]=Ce module externe affiche des informations sur le contexte du langage actuel dans une vue latérale, et met en surbrillance les déclarations et utilisations pertinentes. Comment[gl]=Este engadido mostra información acerca do contexto da linguaxe actual nunha vista lateral, e realza as declaracións e utilizacións relevantes. Comment[it]=Questa estensione mostra le informazioni sul contesto del linguaggio corrente in una vista laterale, mettendo in evidenza le dichiarazioni e gli usi. Comment[nb]=Dette programtillegget viser informasjon om gjeldende språkkontekst i et sidevindu, og fremhever relevante deklarasjoner og bruk. Comment[nds]=Dit Moduul wiest binnen en Sietpaneel Informatschonen över den aktuellen Spraakkontext un heevt wichtig Deklaratschonen un Bruken rut. Comment[nl]=Deze plugin toont informatie over de huidige taalcontext in een zijvak en accentueert relevante declaraties en gebruik. Comment[pl]=Ta wtyczka pokazuje informacje dotyczące kontekstu bieżącego języka w widoku bocznym, podświetla deklaracja i ich użycia. Comment[pt]=Este 'plugin' mostra informações sobre o contexto actual da linguagem numa visão lateral, realçando as declarações e utilizações relevantes. Comment[pt_BR]=Esta extensão mostra informações sobre a linguagem utilizada no momento, numa visão lateral, e destaca declarações relevantes e seus usos. Comment[sl]=Vstavek prikazuje podatke o trenutnem kontekstu in poudari pomembne deklaracije in uporabe. Comment[sv]=Insticksprogrammet visar information om nuvarande språksammanhang i en sidovy, och markerar relevanta deklarationer och användningar. Comment[uk]=За допомогою цього додатка можна переглянути у перегляді на бічній панелі відомості про поточний контекст мови, а також підсвітити пов’язані оголошення і випадки використання. Comment[x-test]=xxThis plugin shows information about the current language context in a side view, and highlights relevant declarations and uses.xx Comment[zh_CN]=此插件在侧边视图中显示关于当前语言上下文的信息,并加亮突出相关的声明和调用。 Comment[zh_TW]=此外掛程式顯示關於目前語言的資訊,並將相關的宣告與使用突顯出來。 Name=Code Browser Name[bg]=Браузър за низове Name[ca]=Navegador de codi Name[ca@valencia]=Navegador de codi Name[cs]=Prohlížeč kódu Name[da]=Kodebrowser Name[de]=Quelltext-Browser Name[el]=Περιηγητής κώδικα Name[en_GB]=Code Browser Name[es]=Navegador de código Name[et]=Koodibrauser Name[fr]=Navigateur de code Name[gl]=Navegador do código Name[it]=Browser del codice Name[ja]=コードブラウザ Name[nb]=Kodeleser Name[nds]=Kodekieker Name[nl]=Broncode-browser Name[pa]=ਕੋਡ ਬਰਾਊਜ਼ਰ Name[pl]=Przeglądarka kodu Name[pt]=Navegador do Código Name[pt_BR]=Navegador de código -Name[ru]=Просмотр кода +Name[ru]=Навигация по коду Name[sl]=Brskalnik po kodi Name[sv]=Kodbläddrare Name[tr]=Kod Tarayıcı Name[uk]=Переглядач коду Name[x-test]=xxCode Browserxx Name[zh_CN]=代码浏览器 Name[zh_TW]=源碼瀏覽器 GenericName=Code Navigation Tool GenericName[bg]=Инструмент за управление на низове GenericName[ca]=Eina de navegació de codi GenericName[ca@valencia]=Eina de navegació de codi GenericName[da]=Værktøj til kodenavigation GenericName[de]=Werkzeug zur Quelltextnavigation GenericName[en_GB]=Code Navigation Tool GenericName[es]=Herramienta de navegación por el código GenericName[et]=Koodi liikumistööriist GenericName[fr]=Outil de navigation de code GenericName[gl]=Utilidade de navegación polo código GenericName[it]=Strumento di navigazione del codice GenericName[ja]=コードナビゲーションツール GenericName[nb]=Kodenavigeringsverktøy GenericName[nds]=Kode-Navigatschoon GenericName[nl]=Hulpmiddel voor navigatie door code GenericName[pl]=Narzędzie do nawigacji w kodzie GenericName[pt]=Ferramenta de Navegação do Código GenericName[pt_BR]=Ferramenta de navegação de código -GenericName[ru]=Инструмент навигации по коду +GenericName[ru]=Инструмент для навигации по коду GenericName[sl]=Orodje za krmarjenje po kodi GenericName[sv]=Kodnavigeringsverktyg GenericName[tr]=Kod Tarama Eklentisi GenericName[uk]=Інструмент навігації кодом GenericName[x-test]=xxCode Navigation Toolxx GenericName[zh_CN]=代码导航工具 GenericName[zh_TW]=源碼導覽工具 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevcontextbrowser X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDE-PluginInfo-Name=kdevcontextbrowser X-KDE-PluginInfo-Author=David Nolden X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Core X-KDevelop-IRequired=org.kdevelop.IQuickOpen X-KDevelop-Mode=GUI diff --git a/plugins/cvs/kdevcvs.desktop b/plugins/cvs/kdevcvs.desktop index e3eae1880d..6bc65525ab 100644 --- a/plugins/cvs/kdevcvs.desktop +++ b/plugins/cvs/kdevcvs.desktop @@ -1,103 +1,103 @@ [Desktop Entry] Type=Service Icon=cervisia Exec=blubb Comment=This plugin integrates CVS to KDevelop Comment[bg]=Тaзи приставка вгражда CVS в KDevelop Comment[ca]=Aquest connector integra CVS en KDevelop Comment[ca@valencia]=Este connector integra CVS en KDevelop Comment[da]=Dette plugin integrerer CVS med KDevelop Comment[de]=Dieses Modul integriert CVS in KDevelop. Comment[el]=Το πρόσθετο αυτό ενσωματώνει το CVS στο KDevelop Comment[en_GB]=This plugin integrates CVS to KDevelop Comment[es]=Este complemento integra CVS en KDevelop Comment[et]=See plugin lõimib CVS-i KDeveloppi Comment[fr]=Ce module externe intègre CVS à KDevelop Comment[gl]=Este engadido integra CVS en KDevelop Comment[it]=Questa estensione integra CVS in KDevelop Comment[ja]=このプラグインは CVS を KDevelop に統合します Comment[lv]=Šis spraudnis integrē CVS iekš KDevelop Comment[nb]=Dette programtillegget integrerer CVS i KDevelop Comment[nds]=Dit Moduul bett CVS na KDevelop in. Comment[nl]=Deze plugin integreert CVS in KDevelop Comment[pl]=Ta wtyczka udostępnia obsługę CVS w KDevelopie Comment[pt]=Este 'plugin' integra o CVS no KDevelop Comment[pt_BR]=Esta extensão integra o CVS ao KDevelop Comment[ro]=Acest modul integrează CVS în KDevelop -Comment[ru]=Этот модуль добавляет поддержку работы с CVS в KDevelop +Comment[ru]=Это расширение добавляет поддержку работы с CVS в KDevelop Comment[sl]=Vstavek v KDevelop integrira CVS Comment[sv]=Insticksprogrammet integrerar CVS i KDevelop Comment[tr]=Bu eklenti CVS uygulamasını KDevelop ile bütünleştirir Comment[uk]=За допомогою цього додатка можна інтегрувати CVS до KDevelop Comment[x-test]=xxThis plugin integrates CVS to KDevelopxx Comment[zh_CN]=此插件对 KDevelop 整合 CVS Comment[zh_TW]=此外掛程式將 CVS 整合進 KDevelop 內。 Name=CVS Support Name[bg]=Поддръжка на CVS Name[ca]=Implementació de CVS Name[ca@valencia]=Implementació de CVS Name[cs]=Podpora CVS Name[da]=CVS-understøttelse Name[de]=CVS-Unterstützung Name[el]=Υποστήριξη CVS Name[en_GB]=CVS Support Name[es]=Implementación de CVS Name[et]=CVS-i toetus Name[fr]=Prise en charge de CVS Name[ga]=Tacaíocht CVS Name[gl]=Soporte de CVS Name[it]=Supporto CVS Name[ja]=CVS サポート Name[nb]=CVS-støtte Name[nds]=CVS-Ünnerstütten Name[nl]=CVS-ondersteuning Name[pa]=CVS ਸਹਿਯੋਗ Name[pl]=Obsługa CVS Name[pt]=Suporte para CVS Name[pt_BR]=Suporte a CVS Name[ru]=Поддержка CVS Name[sl]=Podpora za CVS Name[sv]=Stöd för CVS Name[tr]=CVS Desteği Name[uk]=Підтримка CVS Name[x-test]=xxCVS Supportxx Name[zh_CN]=CVS 支持 Name[zh_TW]=CVS 支援 GenericName=Version Control Support GenericName[bg]=Поддръжка на управление на версиите GenericName[ca]=Implementació del control de versions GenericName[ca@valencia]=Implementació del control de versions GenericName[da]=Understøttelse af versionstyring GenericName[de]=Unterstützung für Versionsverwaltungssysteme GenericName[en_GB]=Version Control Support GenericName[es]=Implementación de Control de Versiones GenericName[et]=Versioonihalduse toetus GenericName[fr]=Prise en charge du contrôle de versions GenericName[gl]=Soporte de sistemas de versións GenericName[it]=Supporto controllo versione GenericName[ja]=バージョン管理のサポート GenericName[nb]=Støtte for versjonskontroll GenericName[nds]=Verschoonkuntrull-Ünnerstütten GenericName[nl]=Ondersteuning voor versiecontrole GenericName[pl]=Obsługa kontroli wersji GenericName[pt]=Suporte ao Controlo de Versões GenericName[pt_BR]=Suporte a controle de versão GenericName[ru]=Поддержка систем контроля версий GenericName[sl]=Podpora za nadzor različic GenericName[sv]=Stöd för versionskontroll GenericName[tr]=Sürüm Kontrol Desteği GenericName[uk]=Підтримка керування версіями GenericName[x-test]=xxVersion Control Supportxx GenericName[zh_CN]=版本控制支持 GenericName[zh_TW]=版本控制支援 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevcvs X-KDE-PluginInfo-Name=kdevcvs X-KDE-PluginInfo-Author=Robert Gruber X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Version Control X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Interfaces=org.kdevelop.IBasicVersionControl X-KDevelop-Mode=GUI diff --git a/plugins/documentswitcher/kdevdocumentswitcher.desktop b/plugins/documentswitcher/kdevdocumentswitcher.desktop index cadcd7ec8b..f33e392daf 100644 --- a/plugins/documentswitcher/kdevdocumentswitcher.desktop +++ b/plugins/documentswitcher/kdevdocumentswitcher.desktop @@ -1,92 +1,92 @@ [Desktop Entry] Type=Service Icon=document-open-recent Exec=blubb Comment=A most-recently-used document switcher for KDevPlatform. Comment[bg]=Последен използван превключвател на документи за KDevPlatform. Comment[ca]=Un commutador del document usat més recentment per KDevPlatform. Comment[ca@valencia]=Un commutador del document usat més recentment per KDevPlatform. Comment[da]=En dokumentskifter til nyligt anvendte til KDevPlatform. Comment[de]=Ein Umschalter zwischen zuletzt geöffneten Dokumenten. Comment[en_GB]=A most-recently-used document switcher for KDevPlatform. Comment[es]=Un cambiador del documento usado más recientemente para KDevPlatform. Comment[et]=KDevPlatformi viimati kasutatud dokumentide vahetaja Comment[fr]=Un commutateur de documents parmi les plus récemment utilisés pour KDevPlatform. Comment[gl]=Un selector entre documentos empregados recentemente para KDevPlatform. Comment[it]=Uno scambia documento utilizzato più di recente per KDevPlatform Comment[nb]=En dokumentbytter for KDevPlatform som behandler sist brukte dokumenter Comment[nds]=En Togrieper op de tolest bruukten Dokmenten för KDevPlatform Comment[nl]=De meest-recent-gebruikte documentwisselaar voor KDevPlatform. Comment[pl]=Przełączanie między ostatnio używanymi dokumentacji w KDevPlatform. Comment[pt]=Um selector dos documentos mais recentes para o KDevPlatform. Comment[pt_BR]=Um seletor dos documentos mais recentes para o KDevPlatform. -Comment[ru]=Переключение между наиболее часто используемыми документами в приложенияхKDevPlatform. +Comment[ru]=Переключение между наиболее часто используемыми документами в приложениях KDevPlatform. Comment[sl]=Vstavek za preklapljanje med nazadnje uporabljenimi dokumenti. Comment[sv]=Byte till senast använda dokument för KDevelop-plattformen. Comment[uk]=Перемикач останніх використаних документів для KDevPlatform. Comment[x-test]=xxA most-recently-used document switcher for KDevPlatform.xx Comment[zh_CN]=KDevPlatform 的最近经常使用文档的切换器。 Comment[zh_TW]=KDevPlatform 上切換最近使用的文件的切換器 Name=Most-Recently-Used Document Switcher Name[bg]=Последният използван превключвател на документи Name[ca]=Commutador del document usat més recentment Name[ca@valencia]=Commutador del document usat més recentment Name[da]=Dokumentskifter til nyligt anvendte Name[de]=Zuletzt-Verwendet-Dokumentumschalter Name[en_GB]=Most-Recently-Used Document Switcher Name[es]=Cambiador del documento usado más recientemente Name[et]=Viimati kasutatud dokumentide vahetaja Name[fr]=Commutateur de documents parmi les plus récemment utilisés Name[gl]=Selector entre documentos empregados recentemente Name[it]=Scambia documento utilizzato più di recente Name[nb]=Sist-brukte dokumentbytter Name[nds]=Togrieper för de tolest bruukten Dokmenten Name[nl]=Meest-recent-gebruikte documentwisselaar Name[pl]=Przełączanie między ostatnio używanymi dokumentami Name[pt]=Selector dos Documentos Usados Mais Recentemente Name[pt_BR]=Seletor dos Documentos Usados Mais Recentemente Name[ru]=Переключатель между наиболее часто используемыми документами Name[sl]=Preklapljanje med nazadnje uporabljenimi dokumenti Name[sv]=Byte till senast använda dokument Name[uk]=Перемикач нещодавно використаних документів Name[x-test]=xxMost-Recently-Used Document Switcherxx Name[zh_CN]=最近经常使用文档切换器 Name[zh_TW]=最近使用的文件切換器 GenericName=Document Switcher GenericName[bg]=Превключвател на документи GenericName[ca]=Commutador de documents GenericName[ca@valencia]=Commutador de documents GenericName[cs]=Přepínač dokumentů GenericName[da]=Dokumentskifter GenericName[de]=Dokumentumschalter GenericName[en_GB]=Document Switcher GenericName[es]=Cambiador de documento GenericName[et]=Dokumendivahetaja GenericName[fr]=Commutateur de documents GenericName[gl]=Selector entre documentos GenericName[it]=Scambia documento GenericName[ja]=文書スイッチャー GenericName[nb]=Dokumentbytter GenericName[nds]=Dokment-Togriep GenericName[nl]=Documentenwisselaar GenericName[pl]=Przełączanie między dokumentami GenericName[pt]=Selector de Documentos GenericName[pt_BR]=Seletor de Documentos GenericName[ru]=Переключатель документов GenericName[sl]=Preklapljanje med dokumenti GenericName[sv]=Dokumentbyte GenericName[tr]=Belge Seçici GenericName[uk]=Інструмент перемикання документів GenericName[x-test]=xxDocument Switcherxx GenericName[zh_CN]=文档切换器 GenericName[zh_TW]=文件切換器 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevdocumentswitcher X-KDE-PluginInfo-Name=kdevdocumentswitcher X-KDE-PluginInfo-Author=Andreas Pakulat X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Utilities X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Mode=GUI diff --git a/plugins/externalscript/kdevexternalscript.desktop b/plugins/externalscript/kdevexternalscript.desktop index 51e2a95d9d..0bddb8f51e 100644 --- a/plugins/externalscript/kdevexternalscript.desktop +++ b/plugins/externalscript/kdevexternalscript.desktop @@ -1,82 +1,84 @@ [Desktop Entry] Type=Service Icon=system-run Exec= Comment=Run external scripts or applications to manipulate the editor contents or do other arbitrary actions. Comment[ca]=Executa scripts externs o aplicacions per a manipular el contingut de l'editor o altres accions arbitràries. Comment[ca@valencia]=Executa scripts externs o aplicacions per a manipular el contingut de l'editor o altres accions arbitràries. Comment[da]=Kør eksterne scripts eller programmer til manipulering af editor-indholdet eller foretag andre handlinger. Comment[de]=Führen Sie externe Skripte oder Programme zum Verändern des Editorinhalts oder für beliebige andere Aktionen aus. Comment[en_GB]=Run external scripts or applications to manipulate the editor contents or do other arbitrary actions. Comment[es]=Ejecutar aplicaciones o scripts externos para manipular el contenido del editor o realizar otras acciones. Comment[et]=Välised skriptid või rakendused, mis võimaldavad muuta redaktori sisu või ette võtta mingeid muid toiminguid. Comment[fr]=Lancer des scripts ou des applications externes pour manipuler le contenu de l'éditeur ou effectuer d'autres actions arbitraires. Comment[it]=Eseguire script o applicazioni esterne per manipolare il contenuto dell'editor o per fare altre azioni. Comment[nb]=Kjør eksterne skripter eller programmer for å behandle redigeringsinnholdet eller utføre andre vilkårlige handlinger. Comment[nds]=Utföhren vun extern Skripten oder Programmen för't Ännern vun den Editor-Inholt oder anner Akschonen. Comment[nl]=Externe scripts of programma's uitvoeren om de inhoud van de bewerker te manipuleren of andere acties uit te voeren. Comment[pt]=Executa programas ou aplicações externa para manipular o conteúdo do editor ou efectua outras acções arbitrárias. Comment[pt_BR]=Execute scripts externos ou aplicativos para manipular os conteúdos do editor ou para fazer outras ações ordinárias. Comment[sl]=Zaganjajte zunanje skripte ali programe, ki upravljajo z vsebino urejevalnika ali pa opravljajo druga poljubna dejanja. Comment[sv]=Kör externa skript eller program för att behandla editorns innehåll eller utför andra godtyckliga åtgärder. Comment[uk]=Запускає зовнішні скрипти або програми для обробки текстових даних редактора або виконання інших потрібних дій. Comment[x-test]=xxRun external scripts or applications to manipulate the editor contents or do other arbitrary actions.xx Comment[zh_CN]=运行外部脚本或应用程序来处理编辑器内容或者执行其它任意动作。 Comment[zh_TW]=執行外部文稿或應用程式來運用編輯器內的內容,或是做各種動作。 Name=External Scripts Name[bg]=Външни скриптове Name[ca]=Scripts externs Name[ca@valencia]=Scripts externs Name[da]=Eksterne scripts Name[de]=Externe Skripte Name[en_GB]=External Scripts Name[es]=Scripts externos Name[et]=Välised skriptid Name[fr]=Scripts externes Name[it]=Script esterni Name[ja]=外部スクリプト Name[nb]=Eksterne skripter Name[nds]=Extern Skripten Name[nl]=Externe scripts Name[pt]=Programas Externos Name[pt_BR]=Scripts externos +Name[ru]=Внешние сценарии Name[sl]=Zunanji skripti Name[sv]=Externa skript Name[uk]=Зовнішні скрипти Name[x-test]=xxExternal Scriptsxx Name[zh_CN]=外部脚本 Name[zh_TW]=外部文稿 GenericName=External Scripts GenericName[bg]=Външни скриптове GenericName[ca]=Scripts externs GenericName[ca@valencia]=Scripts externs GenericName[da]=Eksterne scripts GenericName[de]=Externe Skripte GenericName[en_GB]=External Scripts GenericName[es]=Scripts externos GenericName[et]=Välised skriptid GenericName[fr]=Scripts externes GenericName[it]=Script esterni GenericName[ja]=外部スクリプト GenericName[nb]=Eksterne skripter GenericName[nds]=Extern Skripten GenericName[nl]=Externe scripts GenericName[pt]=Programas Externos GenericName[pt_BR]=Scripts externos +GenericName[ru]=Внешние сценарии GenericName[sl]=Zunanji skripti GenericName[sv]=Externa skript GenericName[uk]=Зовнішні скрипти GenericName[x-test]=xxExternal Scriptsxx GenericName[zh_CN]=外部脚本 GenericName[zh_TW]=外部文稿 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevexternalscript X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDE-PluginInfo-Name=kdevexternalscript X-KDE-PluginInfo-Author=Milian Wolff X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Utilities X-KDevelop-Interfaces=org.kdevelop.IPlugin X-KDevelop-Mode=GUI X-KDevelop-IRequired=org.kdevelop.IOutputView diff --git a/plugins/filemanager/kdevfilemanager.desktop b/plugins/filemanager/kdevfilemanager.desktop index 2919dd760f..e4b0e95091 100644 --- a/plugins/filemanager/kdevfilemanager.desktop +++ b/plugins/filemanager/kdevfilemanager.desktop @@ -1,106 +1,106 @@ [Desktop Entry] Type=Service Icon=system-file-manager Exec=blubb Comment=This plugin brings a filemanager to KDevelop. Comment[bg]=Тaзи приставка вгражда файловия манипулатор към KDevelop. Comment[ca]=Aquest connector proporciona un gestor de fitxers a KDevelop. Comment[ca@valencia]=Este connector proporciona un gestor de fitxers a KDevelop. Comment[da]=Dette plugin bringer filhåndtering til KDevelop. Comment[de]=Dieses Modul integriert einen Datei-Browser in KDevelop. Comment[el]=Αυτό το πρόσθετο ενσωματώνει ένα διαχειριστή αρχείων στο KDevelop Comment[en_GB]=This plugin brings a filemanager to KDevelop. Comment[es]=Este complemento proporciona un gestor de archivos a KDevelop. Comment[et]=See plugin võimaldab kasutada KDevelopis failihaldurit. Comment[fr]=Ce module externe offre un gestionnaire de fichiers pour KDevelop. Comment[gl]=Este engadido incorpora un xestor de ficheiros no KDevelop. Comment[it]=Questa estensione porta un gestore di file in KDevelop. Comment[ja]=このプラグインはファイルマネージャを KDevelop に統合します Comment[lv]=Šis spraudnis nodrošina failu pārvaldnieka funkcionalitāti iekš KDevelop. Comment[nb]=Dette programtillegget tar inn en filbehandler i KDevelop. Comment[nds]=Dit Moduul stellt en Dateipleger för KDevelop praat. Comment[nl]=Deze plugin brengt een bestandsbeheerder in KDevelop. Comment[pl]=Ta wtyczka udostępnia menadżer plików w KDevelopie. Comment[pt]=Este 'plugin' oferece um gestor de ficheiros para o KDevelop. Comment[pt_BR]=Esta extensão provê um gerenciador de arquivos ao KDevelop -Comment[ru]=Этот модуль добавляет диспетчер файлов в KDevelop +Comment[ru]=Это расширение добавляет диспетчер файлов в KDevelop Comment[sl]=Vstavek v KDevelop prinese upravljalnik datotek. Comment[sv]=Insticksprogrammet ger KDevelop en filhanterare. Comment[tr]=Bu eklenti KDevelop için bir dosya yönetici sağlar. Comment[uk]=За допомогою цього додатка можна отримати доступ до менеджера файлів у KDevelop. Comment[x-test]=xxThis plugin brings a filemanager to KDevelop.xx Comment[zh_CN]=此插件对 KDevelop 整合一个文件管理器。 Comment[zh_TW]=此外掛程式讓 KDevelop 可使用檔案管理員。 Name=KDE File Manager Integration Name[bg]=Интегриране на файлов манипулатор на KDE Name[ca]=Integració del gestor de fitxers del KDE Name[ca@valencia]=Integració del gestor de fitxers del KDE Name[da]=Integration af KDE filhåndtering Name[de]=Integration der KDE-Dateiverwaltung Name[en_GB]=KDE File Manager Integration Name[es]=Integración del gestor de archivos de KDE Name[et]=KDE lõimimine failihalduriga Name[fr]=Intégration du gestionnaire de fichiers de KDE Name[gl]=Integración co xestor de ficheiros do KDE Name[it]=Integrazione gestore di file di KDE Name[ja]=KDE のファイルマネージャの統合 Name[nb]=Integrasjon med KDE filbehandler Name[nds]=KDE-Dateiplegerünnerstütten Name[nl]=KDE Bestandsbeheerder-integratie Name[pl]=Integracja menadżera plików KDE Name[pt]=Integração com o Gestor de Ficheiros do KDE Name[pt_BR]=Integração com o gerenciador de arquivos do KDE Name[ru]=Интеграция диспетчера файлов KDE Name[sl]=Integracija KDE-jevega upravljalnika datotek Name[sv]=Integrering med KDE:s filhanterare Name[tr]=KDE Dosya Yöneticisi Bütünleşmesi Name[uk]=Інтеграція засобу керування файлами KDE Name[x-test]=xxKDE File Manager Integrationxx Name[zh_CN]=KDE 文件管理器整合 Name[zh_TW]=KDE 檔案管理員整合 GenericName=File Manager GenericName[bg]=Файлов манипулатор GenericName[ca]=Gestor de fitxers GenericName[ca@valencia]=Gestor de fitxers GenericName[cs]=Správce souborů GenericName[da]=Filhåndtering GenericName[de]=Dateiverwaltung GenericName[el]=Διαχειριστής αρχείων GenericName[en_GB]=File Manager GenericName[eo]=Dosieradministrilo GenericName[es]=Gestor de archivos GenericName[et]=Failihaldur GenericName[fr]=Gestionnaire de fichiers GenericName[ga]=Bainisteoir Comhad GenericName[gl]=Xestor de ficheiros GenericName[it]=Gestore di file GenericName[ja]=ファイルマネージャ GenericName[lt]=Failų tvarkyklė GenericName[lv]=Failu pārvaldnieks GenericName[ms]=Pengurus Fail GenericName[nb]=Filbehandler GenericName[nds]=Dateipleger GenericName[nl]=Bestandsbeheerder GenericName[pa]=ਫਾਇਲ ਮੈਨੇਜਰ GenericName[pl]=Menadżer plików GenericName[pt]=Gestor de Ficheiros GenericName[pt_BR]=Gerenciador de arquivos GenericName[ro]=Gestionar de fișiere GenericName[ru]=Диспетчер файлов GenericName[sl]=Upravljalnik datotek GenericName[sv]=Filhanterare GenericName[tr]=Dosya Yönetici GenericName[uk]=Менеджер файлів GenericName[x-test]=xxFile Managerxx GenericName[zh_CN]=文件管理器 GenericName[zh_TW]=檔案管理員 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevfilemanager X-KDE-PluginInfo-Name=kdevfilemanager X-KDE-PluginInfo-Author=Alexander Dymo X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Core X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Mode=GUI diff --git a/plugins/git/gitplugin.cpp b/plugins/git/gitplugin.cpp index cc256502ee..89c43006ca 100644 --- a/plugins/git/gitplugin.cpp +++ b/plugins/git/gitplugin.cpp @@ -1,1195 +1,1190 @@ /*************************************************************************** * Copyright 2008 Evgeniy Ivanov * * Copyright 2009 Hugo Parente Lima * * Copyright 2010 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "gitplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gitclonejob.h" #include #include #include #include "stashmanagerdialog.h" #include #include #include "gitjob.h" K_PLUGIN_FACTORY(KDevGitFactory, registerPlugin(); ) K_EXPORT_PLUGIN(KDevGitFactory(KAboutData("kdevgit","kdevgit",ki18n("Git"),"0.1",ki18n("A plugin to support git version control systems"), KAboutData::License_GPL))) using namespace KDevelop; namespace { QDir dotGitDirectory(const KUrl& dirPath) { const QFileInfo finfo(dirPath.toLocalFile()); QDir dir = finfo.isDir() ? QDir(dirPath.toLocalFile()) : finfo.absoluteDir(); static const QString gitDir(".git"); while (!dir.exists(gitDir) && dir.cdUp()) {} // cdUp, until there is a sub-directory called .git return dir; } /** * Whenever a directory is provided, change it for all the files in it but not inner directories, * that way we make sure we won't get into recursion, */ static KUrl::List preventRecursion(const KUrl::List& urls) { KUrl::List ret; foreach(const KUrl& url, urls) { QDir d(url.toLocalFile()); if(d.exists()) { QStringList entries = d.entryList(QDir::Files | QDir::NoDotAndDotDot); foreach(const QString& entry, entries) { KUrl entryUrl = d.absoluteFilePath(entry); ret += entryUrl; } } else ret += url; } return ret; } QString toRevisionName(const KDevelop::VcsRevision& rev, QString currentRevision=QString()) { switch(rev.revisionType()) { case VcsRevision::Special: switch(rev.revisionValue().value()) { case VcsRevision::Head: return "^HEAD"; case VcsRevision::Base: return ""; case VcsRevision::Working: return ""; case VcsRevision::Previous: Q_ASSERT(!currentRevision.isEmpty()); return currentRevision + "^1"; case VcsRevision::Start: return ""; case VcsRevision::UserSpecialType: //Not used Q_ASSERT(false && "i don't know how to do that"); } break; case VcsRevision::GlobalNumber: return rev.revisionValue().toString(); case VcsRevision::Date: case VcsRevision::FileNumber: case VcsRevision::Invalid: case VcsRevision::UserSpecialType: Q_ASSERT(false); } return QString(); } QString revisionInterval(const KDevelop::VcsRevision& rev, const KDevelop::VcsRevision& limit) { QString ret; // qDebug() << "prrrrrrrrrr" << toRevisionName(rev, "xxx") << toRevisionName(limit, "yyy"); if(rev.revisionType()==VcsRevision::Special && rev.revisionValue().value()==VcsRevision::Start) //if we want it to the begining just put the revisionInterval ret = toRevisionName(limit, QString()); else { QString dst = toRevisionName(limit); if(dst.isEmpty()) ret = dst; else { QString src = toRevisionName(rev, dst); if(src.isEmpty()) ret = src; else ret = src+".."+dst; } } // qDebug() << "=======>" << ret; return ret; } QDir urlDir(const KUrl& url) { QFileInfo f(url.toLocalFile()); if(f.isDir()) return QDir(url.toLocalFile()); else return f.absoluteDir(); } QDir urlDir(const KUrl::List& urls) { return urlDir(urls.first()); } //TODO: could be improved } GitPlugin::GitPlugin( QObject *parent, const QVariantList & ) : DistributedVersionControlPlugin(parent, KDevGitFactory::componentData()), m_oldVersion(false) { if (KStandardDirs::findExe("git").isEmpty()) { m_hasError = true; m_errorDescription = "git is not installed"; return; } KDEV_USE_EXTENSION_INTERFACE( KDevelop::IBasicVersionControl ) KDEV_USE_EXTENSION_INTERFACE( KDevelop::IDistributedVersionControl ) m_hasError = false; core()->uiController()->addToolView(i18n("Git"), dvcsViewFactory()); setObjectName("Git"); DVcsJob* versionJob = new DVcsJob(QDir::tempPath(), this, KDevelop::OutputJob::Silent); *versionJob << "git" << "--version"; connect(versionJob, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitVersionOutput(KDevelop::DVcsJob*))); ICore::self()->runController()->registerJob(versionJob); } GitPlugin::~GitPlugin() {} bool emptyOutput(DVcsJob* job) { QScopedPointer _job(job); if(job->exec() && job->status()==VcsJob::JobSucceeded) return job->rawOutput().trimmed().isEmpty(); return false; } bool GitPlugin::hasStashes(const QDir& repository) { return !emptyOutput(gitStash(repository, QStringList("list"), KDevelop::OutputJob::Silent)); } bool GitPlugin::hasModifications(const QDir& d) { return !emptyOutput(lsFiles(d, QStringList("-m"), OutputJob::Silent)); } void GitPlugin::additionalMenuEntries(QMenu* menu, const KUrl::List& urls) { m_urls = urls; QDir dir=urlDir(urls); bool modif = hasModifications(dotGitDirectory(urls.first())); bool canApply = !modif && hasStashes(dir); menu->addSeparator()->setText(i18n("Git Stashes")); menu->addAction(i18n("Stash Manager"), this, SLOT(ctxStashManager()))->setEnabled(canApply); menu->addAction(i18n("Push Stash"), this, SLOT(ctxPushStash()))->setEnabled(modif); menu->addAction(i18n("Pop Stash"), this, SLOT(ctxPopStash()))->setEnabled(canApply); } void GitPlugin::ctxPushStash() { VcsJob* job = gitStash(urlDir(m_urls), QStringList(), KDevelop::OutputJob::Verbose); ICore::self()->runController()->registerJob(job); } void GitPlugin::ctxPopStash() { VcsJob* job = gitStash(urlDir(m_urls), QStringList("pop"), KDevelop::OutputJob::Verbose); ICore::self()->runController()->registerJob(job); } void GitPlugin::ctxStashManager() { QPointer d = new StashManagerDialog(urlDir(m_urls), this, 0); d->exec(); delete d; } DVcsJob* GitPlugin::errorsFound(const QString& error, KDevelop::OutputJob::OutputJobVerbosity verbosity=OutputJob::Verbose) { DVcsJob* j = new DVcsJob(QDir::temp(), this, verbosity); *j << "echo" << i18n("error: %1", error) << "-n"; return j; } void GitPlugin::unload() { core()->uiController()->removeToolView( dvcsViewFactory() ); } QString GitPlugin::name() const { return QLatin1String("Git"); } KUrl GitPlugin::repositoryRoot(const KUrl& path) { return KUrl(dotGitDirectory(path).absolutePath()); } bool GitPlugin::isValidDirectory(const KUrl & dirPath) { QDir dir=dotGitDirectory(dirPath); return dir.exists(".git"); } bool GitPlugin::isVersionControlled(const KUrl &path) { QFileInfo fsObject(path.toLocalFile()); if (fsObject.isDir()) { return isValidDirectory(path); } QString filename = fsObject.fileName(); QStringList otherFiles = getLsFiles(fsObject.dir(), QStringList("--") << filename, KDevelop::OutputJob::Silent); return !otherFiles.empty(); } VcsJob* GitPlugin::init(const KUrl &directory) { DVcsJob* job = new DVcsJob(urlDir(directory), this); *job << "git" << "init"; return job; } VcsJob* GitPlugin::createWorkingCopy(const KDevelop::VcsLocation & source, const KUrl& dest, KDevelop::IBasicVersionControl::RecursionMode) { DVcsJob* job = new GitCloneJob(urlDir(dest), this); *job << "git" << "clone" << "--progress" << "--" << source.localUrl().url() << dest; return job; } VcsJob* GitPlugin::add(const KUrl::List& localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { if (localLocations.empty()) return errorsFound(i18n("Did not specify the list of files"), OutputJob::Verbose); DVcsJob* job = new GitJob(dotGitDirectory(localLocations.front()), this); *job << "git" << "add" << "--" << (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); return job; } KDevelop::VcsJob* GitPlugin::status(const KUrl::List& localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { if (localLocations.empty()) return errorsFound(i18n("Did not specify the list of files"), OutputJob::Verbose); DVcsJob* job = new GitJob(urlDir(localLocations), this, OutputJob::Silent); if(m_oldVersion) { *job << "git" << "ls-files" << "-t" << "-m" << "-c" << "-o" << "-d" << "-k" << "--directory"; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitStatusOutput_old(KDevelop::DVcsJob*))); } else { *job << "git" << "status" << "--porcelain"; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitStatusOutput(KDevelop::DVcsJob*))); } *job << "--" << (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); return job; } VcsJob* GitPlugin::diff(const KUrl& fileOrDirectory, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision, VcsDiff::Type type, IBasicVersionControl::RecursionMode recursion) { //TODO: control different types DVcsJob* job = new GitJob(dotGitDirectory(fileOrDirectory), this); *job << "git" << "diff" << "--no-prefix"; QString revstr = revisionInterval(srcRevision, dstRevision); if(!revstr.isEmpty()) *job << revstr; *job << "--" << (recursion == IBasicVersionControl::Recursive ? fileOrDirectory : preventRecursion(fileOrDirectory)); connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitDiffOutput(KDevelop::DVcsJob*))); return job; } VcsJob* GitPlugin::revert(const KUrl::List& localLocations, IBasicVersionControl::RecursionMode recursion) { if(localLocations.isEmpty() ) return errorsFound(i18n("Could not revert changes"), OutputJob::Verbose); DVcsJob* job = new GitJob(dotGitDirectory(localLocations.front()), this); *job << "git" << "checkout" << "--"; *job << (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); return job; } //TODO: git doesn't like empty messages, but "KDevelop didn't provide any message, it may be a bug" looks ugly... //If no files specified then commit already added files VcsJob* GitPlugin::commit(const QString& message, const KUrl::List& localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { if (localLocations.empty() || message.isEmpty()) return errorsFound(i18n("No files or message specified")); QDir dir = dotGitDirectory(localLocations.front()); DVcsJob* job = new DVcsJob(dir, this); KUrl::List files = (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); addNotVersionedFiles(dir, files); *job << "git" << "commit" << "-m" << message; *job << "--" << files; return job; } void GitPlugin::addNotVersionedFiles(const QDir& dir, const KUrl::List& files) { QStringList otherStr = getLsFiles(dir, QStringList() << "--others", KDevelop::OutputJob::Silent); KUrl::List toadd, otherFiles; foreach(const QString& file, otherStr) { KUrl v(dir.absolutePath()); v.addPath(file); otherFiles += v; } //We add the files that are not versioned foreach(const KUrl& file, files) { if(otherFiles.contains(file) && QFileInfo(file.toLocalFile()).isFile()) toadd += file; } if(!toadd.isEmpty()) { VcsJob* job = add(toadd); job->exec(); } } VcsJob* GitPlugin::remove(const KUrl::List& files) { if (files.isEmpty()) return errorsFound(i18n("No files to remove")); QDir dir = dotGitDirectory(files.front()); QStringList otherStr = getLsFiles(dir, QStringList() << "--others" << "--" << files.front().toLocalFile(), KDevelop::OutputJob::Silent); if(otherStr.isEmpty()) { DVcsJob* job = new GitJob(dir, this); *job << "git" << "rm" << "-r"; *job << "--" << files; return job; } else { return new StandardJob(this, KIO::trash(files), KDevelop::OutputJob::Silent); } } VcsJob* GitPlugin::log(const KUrl& localLocation, const KDevelop::VcsRevision& src, const KDevelop::VcsRevision& dst) { DVcsJob* job = new GitJob(dotGitDirectory(localLocation), this); *job << "git" << "log" << "--date=raw"; QString rev = revisionInterval(dst, src); if(!rev.isEmpty()) *job << rev; *job << "--" << localLocation; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), this, SLOT(parseGitLogOutput(KDevelop::DVcsJob*))); return job; } VcsJob* GitPlugin::log(const KUrl& localLocation, const KDevelop::VcsRevision& rev, unsigned long int limit) { DVcsJob* job = new GitJob(dotGitDirectory(localLocation), this, KDevelop::OutputJob::Silent); *job << "git" << "log" << "--date=raw" << toRevisionName(rev, QString()); if(limit>0) *job << QString("-%1").arg(limit); *job << "--" << localLocation; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), this, SLOT(parseGitLogOutput(KDevelop::DVcsJob*))); return job; } KDevelop::VcsJob* GitPlugin::annotate(const KUrl &localLocation, const KDevelop::VcsRevision&) { DVcsJob* job = new GitJob(dotGitDirectory(localLocation), this, KDevelop::OutputJob::Silent); *job << "git" << "blame" << "--porcelain"; *job << "--" << localLocation; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), this, SLOT(parseGitBlameOutput(KDevelop::DVcsJob*))); return job; } void GitPlugin::parseGitBlameOutput(DVcsJob *job) { QVariantList results; VcsAnnotationLine* annotation; QStringList lines = job->output().split('\n'); bool skipNext=false; QMap definedRevisions; for(QStringList::const_iterator it=lines.constBegin(), itEnd=lines.constEnd(); it!=itEnd; ++it) { if(skipNext) { skipNext=false; results += qVariantFromValue(*annotation); continue; } if(it->isEmpty()) continue; QString name = it->left(it->indexOf(' ')); QString value = it->right(it->size()-name.size()-1); kDebug() << "last line" << *it; if(name=="author") annotation->setAuthor(value); else if(name=="author-mail") {} //TODO: do smth with the e-mail? else if(name=="author-tz") {} //TODO: does it really matter? else if(name=="author-time") annotation->setDate(QDateTime::fromTime_t(value.toUInt())); else if(name=="summary") annotation->setCommitMessage(value); else if(name.startsWith("committer")) {} //We will just store the authors else if(name=="previous") {} //We don't need that either else if(name=="filename") { skipNext=true; } else if(name=="boundary") { definedRevisions.insert("boundary", VcsAnnotationLine()); } else { QStringList values = value.split(' '); VcsRevision rev; rev.setRevisionValue(name.left(8), KDevelop::VcsRevision::GlobalNumber); skipNext = definedRevisions.contains(name); if(!skipNext) definedRevisions.insert(name, VcsAnnotationLine()); annotation = &definedRevisions[name]; annotation->setLineNumber(values[1].toInt() - 1); annotation->setRevision(rev); } } job->setResults(results); } DVcsJob* GitPlugin::switchBranch(const QString &repository, const QString &branch) { QDir d(repository); if(hasModifications(d) && KMessageBox::questionYesNo(0, i18n("There are pending changes, do you want to stash them first?"))==KMessageBox::Yes) { QScopedPointer stash(gitStash(d, QStringList(), KDevelop::OutputJob::Verbose)); stash->exec(); } DVcsJob* job = new DVcsJob(d, this); *job << "git" << "checkout" << branch; return job; } DVcsJob* GitPlugin::branch(const QString &repository, const QString &basebranch, const QString &branch, const QStringList &args) { DVcsJob* job = new DVcsJob(QDir(repository), this, KDevelop::OutputJob::Silent); *job << "git" << "branch" << args; *job << "--"; if (!branch.isEmpty()) *job << branch; if (!basebranch.isEmpty()) *job << basebranch; return job; } DVcsJob* GitPlugin::lsFiles(const QDir &repository, const QStringList &args, OutputJob::OutputJobVerbosity verbosity) { DVcsJob* job = new DVcsJob(repository, this, verbosity); *job << "git" << "ls-files" << args; return job; } DVcsJob* GitPlugin::gitStash(const QDir& repository, const QStringList& args, OutputJob::OutputJobVerbosity verbosity) { DVcsJob* job = new DVcsJob(repository, this, verbosity); *job << "git" << "stash" << args; return job; } QString GitPlugin::curBranch(const QString &repository) { kDebug() << "Getting branch list"; QScopedPointer job(new DVcsJob(QDir(repository), this, OutputJob::Silent)); *job << "git" << "symbolic-ref" << "HEAD"; if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) { QString out = job->output().trimmed(); kDebug() << "Getting branch list" << out.right(out.size()-11); return out.right(out.size()-11); } return QString(); } QStringList GitPlugin::branches(const QString &repository) { QStringList branchListDirty; QScopedPointer job(branch(repository)); kDebug() << "Getting branch list"; if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) branchListDirty = job->output().split('\n', QString::SkipEmptyParts); else return QStringList(); QStringList branchList; foreach(QString branch, branchListDirty) { if (branch.contains('*')) { branch = branch.prepend('\n').section("\n*", 1); } else { branch = branch.prepend('\n').section('\n', 1); } branch = branch.trimmed(); branchList< GitPlugin::getAllCommits(const QString &repo) { - static bool hasHash = false; - if (!hasHash) - { - initBranchHash(repo); - hasHash = true; - } + initBranchHash(repo); + QStringList args; args << "--all" << "--pretty" << "--parents"; QScopedPointer job(gitRevList(repo, args)); bool ret = job->exec(); Q_ASSERT(ret && job->status()==VcsJob::JobSucceeded && "TODO: provide a fall back in case of failing"); QStringList commits = job->output().split('\n', QString::SkipEmptyParts); static QRegExp rx_com("commit \\w{40,40}"); QListcommitList; DVcsEvent item; //used to keep where we have empty/cross/branch entry //true if it's an active branch (then cross or branch) and false if not QVector additionalFlags(branchesShas.count()); - foreach(int flag, additionalFlags) - flag = false; + additionalFlags.fill(false); //parse output for(int i = 0; i < commits.count(); ++i) { if (commits[i].contains(rx_com)) { kDebug() << "commit found in " << commits[i]; item.setCommit(commits[i].section(' ', 1, 1).trimmed()); // kDebug() << "commit is: " << commits[i].section(' ', 1); QStringList parents; QString parent = commits[i].section(' ', 2); int section = 2; while (!parent.isEmpty()) { /* kDebug() << "Parent is: " << parent;*/ parents.append(parent.trimmed()); section++; parent = commits[i].section(' ', section); } item.setParents(parents); //Avoid Merge string while (!commits[i].contains("Author: ")) ++i; item.setAuthor(commits[i].section("Author: ", 1).trimmed()); // kDebug() << "author is: " << commits[i].section("Author: ", 1); item.setDate(commits[++i].section("Date: ", 1).trimmed()); // kDebug() << "date is: " << commits[i].section("Date: ", 1); QString log; i++; //next line! while (i < commits.count() && !commits[i].contains(rx_com)) log += commits[i++]; --i; //while took commit line item.setLog(log.trimmed()); // kDebug() << "log is: " << log; //mask is used in CommitViewDelegate to understand what we should draw for each branch QList mask; //set mask (properties for each graph column in row) for(int i = 0; i < branchesShas.count(); ++i) { kDebug()<<"commit: " << item.getCommit(); if (branchesShas[i].contains(item.getCommit())) { mask.append(item.getType()); //we set type in setParents //check if parent from the same branch, if not then we have found a root of the branch //and will use empty column for all futher (from top to bottom) revisions //FIXME: we should set CROSS between parent and child (and do it when find merge point) additionalFlags[i] = false; foreach(const QString &sha, item.getParents()) { if (branchesShas[i].contains(sha)) additionalFlags[i] = true; } if (additionalFlags[i] == false) item.setType(DVcsEvent::INITIAL); //hasn't parents from the same branch, used in drawing } else { if (additionalFlags[i] == false) mask.append(DVcsEvent::EMPTY); else mask.append(DVcsEvent::CROSS); } kDebug() << "mask " << i << "is " << mask[i]; } item.setProperties(mask); commitList.append(item); } } //find and set merges, HEADS, require refactoring! for(QList::iterator iter = commitList.begin(); iter != commitList.end(); ++iter) { QStringList parents = iter->getParents(); //we need only only child branches if (parents.count() != 1) break; QString parent = parents[0]; QString commit = iter->getCommit(); bool parent_checked = false; int heads_checked = 0; for(int i = 0; i < branchesShas.count(); ++i) { //check parent if (branchesShas[i].contains(commit)) { if (!branchesShas[i].contains(parent)) { //parent and child are not in same branch //since it is list, than parent has i+1 index //set CROSS and HCROSS for(QList::iterator f_iter = iter; f_iter != commitList.end(); ++f_iter) { if (parent == f_iter->getCommit()) { for(int j = 0; j < i; ++j) { if(branchesShas[j].contains(parent)) f_iter->setPropetry(j, DVcsEvent::MERGE); else f_iter->setPropetry(j, DVcsEvent::HCROSS); } f_iter->setType(DVcsEvent::MERGE); f_iter->setPropetry(i, DVcsEvent::MERGE_RIGHT); kDebug() << parent << " is parent of " << commit; kDebug() << f_iter->getCommit() << " is merge"; parent_checked = true; break; } else f_iter->setPropetry(i, DVcsEvent::CROSS); } } } //mark HEADs if (!branchesShas[i].empty() && commit == branchesShas[i][0]) { iter->setType(DVcsEvent::HEAD); iter->setPropetry(i, DVcsEvent::HEAD); heads_checked++; kDebug() << "HEAD found"; } //some optimization if (heads_checked == branchesShas.count() && parent_checked) break; } } return commitList; } void GitPlugin::initBranchHash(const QString &repo) { QStringList branches = GitPlugin::branches(repo); kDebug() << "BRANCHES: " << branches; //Now root branch is the current branch. In future it should be the longest branch //other commitLists are got with git-rev-lits branch ^br1 ^ br2 QString root = GitPlugin::curBranch(repo); QScopedPointer job(gitRevList(repo, QStringList(root))); bool ret = job->exec(); Q_ASSERT(ret && job->status()==VcsJob::JobSucceeded && "TODO: provide a fall back in case of failing"); QStringList commits = job->output().split('\n', QString::SkipEmptyParts); // kDebug() << "\n\n\n commits" << commits << "\n\n\n"; branchesShas.append(commits); foreach(const QString &branch, branches) { if (branch == root) continue; QStringList args(branch); foreach(const QString &branch_arg, branches) { if (branch_arg != branch) //man gitRevList for '^' args<<'^' + branch_arg; } QScopedPointer job(gitRevList(repo, args)); bool ret = job->exec(); Q_ASSERT(ret && job->status()==VcsJob::JobSucceeded && "TODO: provide a fall back in case of failing"); QStringList commits = job->output().split('\n', QString::SkipEmptyParts); // kDebug() << "\n\n\n commits" << commits << "\n\n\n"; branchesShas.append(commits); } } //Actually we can just copy the output without parsing. So it's a kind of draft for future void GitPlugin::parseLogOutput(const DVcsJob * job, QList& commits) const { // static QRegExp rx_sep( "[-=]+" ); // static QRegExp rx_date( "date:\\s+([^;]*);\\s+author:\\s+([^;]*).*" ); static QRegExp rx_com( "commit \\w{1,40}" ); QStringList lines = job->output().split('\n', QString::SkipEmptyParts); DVcsEvent item; QString commitLog; for (int i=0; i commits; static QRegExp commitRegex( "^commit (\\w{8})\\w{32}" ); static QRegExp infoRegex( "^(\\w+):(.*)" ); QString contents = job->output(); QTextStream s(&contents); VcsEvent item; QString message; bool pushCommit = false; while (!s.atEnd()) { QString line = s.readLine(); if (commitRegex.exactMatch(line)) { if (pushCommit) { item.setMessage(message.trimmed()); commits.append(QVariant::fromValue(item)); } else { pushCommit = true; } VcsRevision rev; rev.setRevisionValue(commitRegex.cap(1), KDevelop::VcsRevision::GlobalNumber); item.setRevision(rev); message.clear(); } else if (infoRegex.exactMatch(line)) { QString cap1 = infoRegex.cap(1); if (cap1 == "Author") { item.setAuthor(infoRegex.cap(2).trimmed()); } else if (cap1 == "Date") { item.setDate(QDateTime::fromTime_t(infoRegex.cap(2).trimmed().split(' ')[0].toUInt())); } } else if (line.startsWith(" ")) { message += line.remove(0, 4); message += '\n'; } } item.setMessage(message.trimmed()); commits.append(QVariant::fromValue(item)); job->setResults(commits); } void GitPlugin::parseGitDiffOutput(DVcsJob* job) { VcsDiff diff; diff.setDiff(job->output()); diff.setBaseDiff(repositoryRoot(KUrl(job->directory().absolutePath()))); job->setResults(qVariantFromValue(diff)); } static VcsStatusInfo::State lsfilesToState(char id) { switch(id) { case 'H': return VcsStatusInfo::ItemUpToDate; //Cached case 'S': return VcsStatusInfo::ItemUpToDate; //Skip work tree case 'M': return VcsStatusInfo::ItemHasConflicts; //unmerged case 'R': return VcsStatusInfo::ItemDeleted; //removed/deleted case 'C': return VcsStatusInfo::ItemModified; //modified/changed case 'K': return VcsStatusInfo::ItemDeleted; //to be killed case '?': return VcsStatusInfo::ItemUnknown; //other } Q_ASSERT(false); return VcsStatusInfo::ItemUnknown; } void GitPlugin::parseGitStatusOutput_old(DVcsJob* job) { QStringList outputLines = job->output().split('\n', QString::SkipEmptyParts); KUrl d = job->directory().absolutePath(); QMap allStatus; foreach(const QString& line, outputLines) { VcsStatusInfo::State status = lsfilesToState(line[0].toAscii()); KUrl url = d; url.addPath(line.right(line.size()-2)); allStatus[url] = status; } QVariantList statuses; QMap< KUrl, VcsStatusInfo::State >::const_iterator it = allStatus.constBegin(), itEnd=allStatus.constEnd(); for(; it!=itEnd; ++it) { VcsStatusInfo status; status.setUrl(it.key()); status.setState(it.value()); statuses.append(qVariantFromValue(status)); } job->setResults(statuses); } void GitPlugin::parseGitStatusOutput(DVcsJob* job) { QStringList outputLines = job->output().split('\n', QString::SkipEmptyParts); const KUrl workingDir = job->directory().absolutePath(); const KUrl dotGit = dotGitDirectory(workingDir).absolutePath(); QVariantList statuses; QList processedFiles; foreach(const QString& line, outputLines) { //every line is 2 chars for the status, 1 space then the file desc QString curr=line.right(line.size()-3); QString state = line.left(2); int arrow = curr.indexOf("-> "); if(arrow>=0) curr = curr.right(curr.size()-arrow-3); KUrl fileUrl = dotGit; fileUrl.addPath(curr); processedFiles.append(fileUrl); VcsStatusInfo status; status.setUrl(fileUrl); status.setState(messageToState(state)); kDebug() << "Checking git status for " << line << curr << messageToState(state); statuses.append(qVariantFromValue(status)); } QStringList paths; QStringList oldcmd=job->dvcsCommand(); QStringList::const_iterator it=oldcmd.constBegin()+oldcmd.indexOf("--")+1, itEnd=oldcmd.constEnd(); for(; it!=itEnd; ++it) paths += *it; //here we add the already up to date files QStringList files = getLsFiles(job->directory(), QStringList() << "-c" << "--" << paths, OutputJob::Silent); foreach(const QString& file, files) { KUrl fileUrl = workingDir; fileUrl.addPath(file); if(!processedFiles.contains(fileUrl)) { VcsStatusInfo status; status.setUrl(fileUrl); status.setState(VcsStatusInfo::ItemUpToDate); statuses.append(qVariantFromValue(status)); } } job->setResults(statuses); } void GitPlugin::parseGitVersionOutput(DVcsJob* job) { QStringList versionString = job->output().trimmed().split(' ').last().split('.'); static QList minimumVersion = QList() << 1 << 7; kDebug() << "checking git version" << versionString << "against" << minimumVersion; m_oldVersion = false; foreach(int num, minimumVersion) { QString curr = versionString.takeFirst(); int valcurr = curr.toInt(); m_oldVersion |= valcurr job(lsFiles(directory, args, verbosity)); if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) return job->output().split('\n', QString::SkipEmptyParts); return QStringList(); } DVcsJob* GitPlugin::gitRevParse(const QString &repository, const QStringList &args, KDevelop::OutputJob::OutputJobVerbosity verbosity) { DVcsJob* job = new DVcsJob(QDir(repository), this, verbosity); *job << "git" << "rev-parse" << args; return job; } DVcsJob* GitPlugin::gitRevList(const QString &repository, const QStringList &args) { - DVcsJob* job = new DVcsJob(QDir(repository), this); + DVcsJob* job = new DVcsJob(QDir(repository), this, KDevelop::OutputJob::Silent); { *job << "git" << "rev-list" << args; return job; } } VcsStatusInfo::State GitPlugin::messageToState(const QString& msg) { Q_ASSERT(msg.size()==1 || msg.size()==2); VcsStatusInfo::State ret = VcsStatusInfo::ItemUnknown; if(msg.contains('U') || msg == "AA" || msg == "DD") ret = VcsStatusInfo::ItemHasConflicts; else switch(msg[0].toAscii()) { case 'M': ret = VcsStatusInfo::ItemModified; break; case 'A': ret = VcsStatusInfo::ItemAdded; break; case 'R': case 'C': ret = VcsStatusInfo::ItemModified; break; case ' ': ret = msg[1] == 'M' ? VcsStatusInfo::ItemModified : VcsStatusInfo::ItemDeleted; break; case 'D': ret = VcsStatusInfo::ItemDeleted; break; case '?': ret = VcsStatusInfo::ItemUnknown; break; default: kDebug() << "Git status not identified:" << msg; break; } return ret; } StandardJob::StandardJob(IPlugin* parent, KJob* job, OutputJob::OutputJobVerbosity verbosity) : VcsJob(parent, verbosity) , m_job(job) , m_plugin(parent) , m_status(JobNotStarted) {} void StandardJob::start() { connect(m_job, SIGNAL(result(KJob*)), SLOT(result(KJob*))); m_job->start(); m_status=JobRunning; } void StandardJob::result(KJob* job) { m_status=job->error() == 0? JobSucceeded : JobFailed; emitResult(); } VcsJob* GitPlugin::copy(const KUrl& localLocationSrc, const KUrl& localLocationDstn) { //TODO: Probably we should "git add" after return new StandardJob(this, KIO::copy(localLocationSrc, localLocationDstn), KDevelop::OutputJob::Silent); } VcsJob* GitPlugin::move(const KUrl& source, const KUrl& destination) { QDir dir = urlDir(source); QStringList otherStr = getLsFiles(dir, QStringList() << "--others" << "--" << source.toLocalFile(), KDevelop::OutputJob::Silent); if(otherStr.isEmpty()) { DVcsJob* job = new DVcsJob(dir, this, KDevelop::OutputJob::Verbose); *job << "git" << "mv" << source.toLocalFile() << destination.toLocalFile(); return job; } else { return new StandardJob(this, KIO::move(source, destination), KDevelop::OutputJob::Silent); } } void GitPlugin::parseGitRepoLocationOutput(DVcsJob* job) { job->setResults(qVariantFromValue(KUrl(job->output()))); } VcsJob* GitPlugin::repositoryLocation(const KUrl& localLocation) { DVcsJob* job = new DVcsJob(urlDir(localLocation), this); //Probably we should check first if origin is the proper remote we have to use but as a first attempt it works *job << "git" << "config" << "remote.origin.url"; connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitRepoLocationOutput(KDevelop::DVcsJob*))); return job; } VcsJob* GitPlugin::pull(const KDevelop::VcsLocation& localOrRepoLocationSrc, const KUrl& localRepositoryLocation) { DVcsJob* job = new DVcsJob(urlDir(localRepositoryLocation), this); job->setCommunicationMode(KProcess::MergedChannels); *job << "git" << "pull"; if(!localOrRepoLocationSrc.localUrl().isEmpty()) *job << localOrRepoLocationSrc.localUrl().url(); return job; } VcsJob* GitPlugin::push(const KUrl& localRepositoryLocation, const KDevelop::VcsLocation& localOrRepoLocationDst) { DVcsJob* job = new DVcsJob(urlDir(localRepositoryLocation), this); job->setCommunicationMode(KProcess::MergedChannels); *job << "git" << "push"; if(!localOrRepoLocationDst.localUrl().isEmpty()) *job << localOrRepoLocationDst.localUrl().url(); return job; } VcsJob* GitPlugin::resolve(const KUrl::List& localLocations, IBasicVersionControl::RecursionMode recursion) { return add(localLocations, recursion); } VcsJob* GitPlugin::update(const KUrl::List& localLocations, const KDevelop::VcsRevision& rev, IBasicVersionControl::RecursionMode recursion) { if(rev.revisionType()==VcsRevision::Special && rev.revisionValue().value()==VcsRevision::Head) { return pull(VcsLocation(), localLocations.first()); } else { DVcsJob* job = new DVcsJob(urlDir(localLocations.first().toLocalFile()), this); { //Probably we should check first if origin is the proper remote we have to use but as a first attempt it works *job << "git" << "checkout" << rev.revisionValue().toString() << "--"; *job << (recursion == IBasicVersionControl::Recursive ? localLocations : preventRecursion(localLocations)); return job; } } } class GitVcsLocationWidget : public KDevelop::StandardVcsLocationWidget { public: GitVcsLocationWidget(QWidget* parent = 0, Qt::WindowFlags f = 0) : StandardVcsLocationWidget(parent, f) {} virtual bool isCorrect() const { return true; } }; KDevelop::VcsLocationWidget* GitPlugin::vcsLocation(QWidget* parent) const { return new GitVcsLocationWidget(parent); } bool GitPlugin::hasError() const { return m_hasError; } QString GitPlugin::errorDescription() const { return m_errorDescription; } diff --git a/plugins/git/kdevgit.desktop b/plugins/git/kdevgit.desktop index 5203b8b0ca..c06364e263 100644 --- a/plugins/git/kdevgit.desktop +++ b/plugins/git/kdevgit.desktop @@ -1,101 +1,101 @@ [Desktop Entry] Type=Service Exec=blubb Icon=git Comment=This plugin integrates Git to KDevelop Comment[bg]=Тaзи приставка вгражда Git в KDevelop Comment[ca]=Aquest connector integra Git en KDevelop Comment[ca@valencia]=Este connector integra Git en KDevelop Comment[da]=Dette plugin integrerer Git med KDevelop Comment[de]=Dieses Modul integriert Git in KDevelop. Comment[el]=Αυτό το πρόσθετο ενσωματώνει το Git στο KDevelop Comment[en_GB]=This plugin integrates Git to KDevelop Comment[es]=Este complemento integra Git en KDevelop Comment[et]=See plugin lõimib Giti KDevelopiga Comment[fr]=Ce module externe intègre Git dans KDevelop Comment[gl]=Este engadido integra Git en KDevelop Comment[it]=Questa estensione integra Git in KDevelop Comment[ja]=このプラグインは Git を KDevelop に統合します Comment[lv]=Šis spraudnis integrē Git iekš KDevelop Comment[nb]=Dette programtillegget integrerer Git i KDevelop Comment[nds]=Dit Moduul bett Git na KDevelop in Comment[nl]=Deze plugin integreert Git in KDevelop Comment[pt]=Este 'plugin' integra o Git no KDevelop Comment[pt_BR]=Este plug-in integra o Git ao KDevelop Comment[ro]=Acest modul integrează Git în KDevelop -Comment[ru]=Этот модуль интегрирует Git в KDevelop +Comment[ru]=Это расширение интегрирует Git в KDevelop Comment[sl]=Vstavek v KDevelop integrira Git Comment[sv]=Insticksprogrammet integrerar Git i KDevelop Comment[tr]=Bu eklenti Git uygulamasını KDevelop ile bütünleştirir Comment[uk]=Цей додаток призначено для забезпечення підтримки Git у KDevelop Comment[x-test]=xxThis plugin integrates Git to KDevelopxx Comment[zh_CN]=此插件对 KDevelop 整合 Git Comment[zh_TW]=此外掛程式將 Git 整合進 KDevelop Name=Git Support Name[bg]=Поддръжка на Git Name[ca]=Implementació de Git Name[ca@valencia]=Implementació de Git Name[da]=Git-understøttelse Name[de]=Git-Unterstützung Name[el]=Υποστήριξη Git Name[en_GB]=Git Support Name[es]=Implementación de Git Name[et]=Giti toetus Name[fr]=Prise en charge de Git Name[ga]=Tacaíocht Git Name[gl]=Soporte de GIT Name[it]=Supporto Git Name[ja]=Git サポート Name[nb]=Git-støtte Name[nds]=Git-Ünnerstütten Name[nl]=Git-ondersteuning Name[pa]=Git ਸਹਿਯੋਗ Name[pt]=Suporte para o Git Name[pt_BR]=Suporte ao Git Name[ru]=Поддержка Git Name[sl]=Podpora za Git Name[sv]=Stöd för Git Name[tr]=Git Desteği Name[uk]=Підтримка Git Name[x-test]=xxGit Supportxx Name[zh_CN]=Git 支持 Name[zh_TW]=Git 支援 GenericName=Version Control Support GenericName[bg]=Поддръжка на управление на версиите GenericName[ca]=Implementació del control de versions GenericName[ca@valencia]=Implementació del control de versions GenericName[da]=Understøttelse af versionstyring GenericName[de]=Unterstützung für Versionsverwaltungssysteme GenericName[en_GB]=Version Control Support GenericName[es]=Implementación de Control de Versiones GenericName[et]=Versioonihalduse toetus GenericName[fr]=Prise en charge du contrôle de versions GenericName[gl]=Soporte de sistemas de versións GenericName[it]=Supporto controllo versione GenericName[ja]=バージョン管理のサポート GenericName[nb]=Støtte for versjonskontroll GenericName[nds]=Verschoonkuntrull-Ünnerstütten GenericName[nl]=Ondersteuning voor versiecontrole GenericName[pl]=Obsługa kontroli wersji GenericName[pt]=Suporte ao Controlo de Versões GenericName[pt_BR]=Suporte a controle de versão GenericName[ru]=Поддержка систем контроля версий GenericName[sl]=Podpora za nadzor različic GenericName[sv]=Stöd för versionskontroll GenericName[tr]=Sürüm Kontrol Desteği GenericName[uk]=Підтримка керування версіями GenericName[x-test]=xxVersion Control Supportxx GenericName[zh_CN]=版本控制支持 GenericName[zh_TW]=版本控制支援 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevgit X-KDE-PluginInfo-Name=kdevgit X-KDE-PluginInfo-Author=Evgeniy Ivanov X-KDE-PluginInfo-Email=powerfox@kde.ru X-KDE-PluginInfo-Version=0.9 X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Version Control X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Interfaces=org.kdevelop.IBasicVersionControl, org.kdevelop.IDistributedVersionControl X-KDevelop-Mode=GUI diff --git a/plugins/grepview/kdevgrepview.desktop b/plugins/grepview/kdevgrepview.desktop index dacd1c431f..1165d896db 100644 --- a/plugins/grepview/kdevgrepview.desktop +++ b/plugins/grepview/kdevgrepview.desktop @@ -1,70 +1,73 @@ [Desktop Entry] Type=Service Exec=blubb Comment=Allows fast searching of multiple files using patterns or regular expressions. And allow to replace it too. Comment[da]=Muliggør hurtig søgning af flere filer med brug af mønstre eller regulære udtryk - og muliggør også erstatning. Comment[es]=Permite búsquedas rápidas de múltiples archivos usando patrones o expresiones regulares. También permite sustituciones. Comment[et]=Lubab mustreid või regulaaravaldisi kasutades kiiresti paljudes failides teksti otsida, samuti asendada. Comment[fr]=Permet la recherche rapide de fichiers multiples à l'aide de motifs ou d'expressions rationnelles, et d'effectuer le remplacement aussi. +Comment[it]=Consente la ricerca veloce di file multipli usando espressioni regolari o modelli. E consente anche di sostituirli. Comment[nb]=Gjør raskt søk i flere filer mulig ved hjelp av mønstre eller regulære uttrykk. Og kan erstatte også. Comment[nds]=Stellt gau Dörsöken vun mehr Dateien mit Söökmustern oder reguleer Utdrück praat. Utwesseln warrt ok ünnerstütt. Comment[nl]=Staat toe snel te zoeken naar meerdere bestanden met gebruik van patronen of reguliere expressies. En staat ook vervanging toe. Comment[pt]=Permite pesquisar rapidamente em vários ficheiros, usando padrões ou expressões regulares. Também permite efectuar logo substituições. Comment[pt_BR]=Permite pesquisar rapidamente em vários arquivos, usando padrões ou expressões regulares. E permite realizar substituições também. Comment[sv]=Tillåter snabb sökning i flera filer med mönster eller reguljära uttryck, och tillåter dessutom ersättning av uttrycket. Comment[uk]=Надає можливості швидкого пошуку та заміни у декількох файлів на основі шаблонів або формальних виразів. Comment[x-test]=xxAllows fast searching of multiple files using patterns or regular expressions. And allow to replace it too.xx Comment[zh_TW]=允許使用樣式或正規表示式來快速搜尋與取代多個檔案。 Name=Find/Replace In Files Name[da]=Find/erstat i filer Name[de]=In Dateien suchen/ersetzen Name[en_GB]=Find/Replace In Files Name[es]=Buscar/sustituir en archivos Name[et]=Failides otsimine ja asendamine Name[fr]=Chercher / Remplacer dans les fichiers +Name[it]=Trova/Sostituisci nei file Name[nb]=Finn/erstatt i filer Name[nds]=Söken un Utwesseln in Dateien Name[nl]=Zoeken/vervangen in bestanden Name[pt]=Procurar/Substituir nos Ficheiros Name[pt_BR]=Procurar/Substituir nos arquivos +Name[ru]=Поиск и замена в файлах Name[sv]=Sök eller ersätt i filer Name[uk]=Пошук або заміна у файлах Name[x-test]=xxFind/Replace In Filesxx Name[zh_TW]=在檔案中尋找/取代 GenericName=Search Tool GenericName[bg]=Инструмент за търсене GenericName[ca]=Eina de cerca GenericName[ca@valencia]=Eina de cerca GenericName[da]=Søgeværktøj GenericName[de]=Such-Werkzeug GenericName[en_GB]=Search Tool GenericName[es]=Herramienta de búsqueda GenericName[et]=Otsimise tööriist GenericName[fr]=Outil de recherche GenericName[ga]=Uirlis Chuardaigh GenericName[gl]=Utilidade de procura GenericName[it]=Strumento ricerca GenericName[ja]=検索ツール GenericName[nb]=Søkeverktøy GenericName[nds]=Söökwarktüüch GenericName[nl]=Hulpmiddel voor zoeken GenericName[pl]=Narzędzie wyszukiwania GenericName[pt]=Ferramenta de Pesquisa GenericName[pt_BR]=Ferramenta de Pesquisa GenericName[ru]=Инструмент поиска GenericName[sl]=Orodje za iskanje GenericName[sv]=Sökverktyg GenericName[tr]=Arama Aracı GenericName[uk]=Інструмент пошуку GenericName[x-test]=xxSearch Toolxx GenericName[zh_CN]=搜索工具 GenericName[zh_TW]=搜尋工具 Icon=kfind ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevgrepview X-KDE-PluginInfo-Name=kdevgrepview X-KDE-PluginInfo-Category=Utilities X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-IRequired=org.kdevelop.IStatus X-KDevelop-Mode=GUI diff --git a/plugins/konsole/kdevkonsoleview.desktop b/plugins/konsole/kdevkonsoleview.desktop index f496b0c4d0..492729ff37 100644 --- a/plugins/konsole/kdevkonsoleview.desktop +++ b/plugins/konsole/kdevkonsoleview.desktop @@ -1,92 +1,92 @@ [Desktop Entry] Type=Service Exec=blubb Comment=This plugin provides KDevelop with an embedded konsole for quick and easy command line access. Comment[bg]=Тaзи приставка осигурява на KDevelop вградена конзола за лесен достъп от команден ред. Comment[ca]=Aquest connector proporciona a KDevelop un Konsole incrustat per accedir de manera ràpida i fàcil a la línia d'ordres. Comment[ca@valencia]=Este connector proporciona a KDevelop un Konsole incrustat per accedir de manera ràpida i fàcil a la línia d'ordes. Comment[da]=Dette plugin giver KDevelop en indlejret konsol for hurtig og nem kommandolinje adgang. Comment[de]=Dieses Modul stattet KDevelop mit einer eingebetteten Konsole zum einfachen Zugriff auf die Befehlszeile aus. Comment[el]=Αυτό το πρόσθετο προσφέρει στο KDevelop μια ενσωματωμένη konsole για γρήγορη και εύκολη πρόσβαση σε γραμμή εντολών. Comment[en_GB]=This plugin provides KDevelop with an embedded konsole for quick and easy command line access. Comment[es]=Este complemento proporciona a KDevelop una consola empotrada para acceder rápida y fácilmente a la línea de órdenes. Comment[et]=See plugin pakub KDevelopile põimitud konsooli käsurea kiireks ja lihtsaks kasutamiseks. Comment[fr]=Ce module externe fournit à KDevelop une console intégrée permettant un accès rapide et aisé à la ligne de commande. Comment[gl]=Este engadido fornécelle a KDevelop un konsole integrado para dispor de acceso rápido e sinxelo á liña de ordes. Comment[it]=Questa estensione dota KDevelop di una console integrata per una rapido e facile accesso alla riga di comando. Comment[ja]=コマンドラインに簡単に素早くアクセスできるように KDevelop に組み込み Konsole を提供します Comment[nb]=Dette programtillegget gir KDevelop en innebygget konsole for rask og lett tilgang til kommandolinja. Comment[nds]=Dit Moduul stellt en inbett Konsole för KDevelop praat, wat gau un eenfach Togriep op de Konsool verlöövt. Comment[nl]=Deze plugin biedt KDevelop een ingebed konsole voor snel en gemakkelijk toegang tot commandoregels. Comment[pl]=Ta wtyczka dodaje do KDevelopa wbudowaną konsolę umożliwiając szybki i łatwy dostęp do linii komend. Comment[pt]=Este 'plugin' oferece ao KDevelop uma consola incorporada para um acesso rápido e simples à linha de comandos. Comment[pt_BR]=Esta extensão provê ao KDevelop um terminal embutido para acesso rápido e fácil a linha de comando. -Comment[ru]=Этот модуль добавляет в KDevelop встроенный терминал для быстрого и лёгкого доступа к командной строке +Comment[ru]=Это расширение добавляет в KDevelop встроенный терминал для быстрого и лёгкого доступа к командной строке Comment[sl]=Vstavek v KDevelop prinaša vgrajeno konzolo za hiter in preprost dostop do ukazne vrstice. Comment[sv]=Insticksprogrammet ger KDevelop en inbyggd terminal för snabb och enkel åtkomst av kommandoraden. Comment[uk]=За допомогою цього додатка у KDevelop можна буде скористатися вбудованою konsole, яка пришвидшить і полегшить доступ до командного рядка. Comment[x-test]=xxThis plugin provides KDevelop with an embedded konsole for quick and easy command line access.xx Comment[zh_CN]=此插件为 KDevelop 提供了一个快速方便地访问命令行的嵌入式控制台。 Comment[zh_TW]=此外掛程式提供 KDevelop 一個嵌入式的 konsole,能快速地使用命令列。 Name=Konsole Integration Name[bg]=Интегриране на конзола Name[ca]=Integració del Konsole Name[ca@valencia]=Integració del Konsole Name[da]=Integration af Konsole Name[de]=Konsole-Integration Name[en_GB]=Konsole Integration Name[es]=Integración de la consola Name[et]=Lõimimine Konsooliga Name[fr]=Intégration de Konsole Name[gl]=Integración con Konsole Name[it]=Integrazione Konsole Name[ja]=Konsole の統合 Name[nb]=Konsole-integrering Name[nds]=Konsool-Inbinnen Name[nl]=Console-integratie Name[pl]=Integracja Konsoli Name[pt]=Integração com o Konsole Name[pt_BR]=Integração com o Konsole Name[ru]=Интеграция Konsole Name[sl]=Integracije konzole Name[sv]=Integrering av Konsole Name[tr]=Konsole Bütünleşmesi Name[uk]=Інтеграція Konsole Name[x-test]=xxKonsole Integrationxx Name[zh_CN]=Konsole 整合 Name[zh_TW]=Konsole 整合 GenericName=Terminal Integration GenericName[bg]=Интегриране на терминал GenericName[ca]=Integració del terminal GenericName[ca@valencia]=Integració del terminal GenericName[da]=Integration af terminal GenericName[de]=Terminal-Integration GenericName[en_GB]=Terminal Integration GenericName[es]=Integración de la terminal GenericName[et]=Lõimimine terminaliga GenericName[fr]=Intégration du terminal GenericName[gl]=Integración co terminal GenericName[it]=Integrazione terminale GenericName[ja]=ターミナルの統合 GenericName[nb]=Terminalintegrering GenericName[nds]=Konsool-Inbinnen GenericName[nl]=Terminal-integratie GenericName[pl]=Integracja terminala GenericName[pt]=Integração com o Terminal GenericName[pt_BR]=Integração com o terminal GenericName[ru]=Интеграция терминала GenericName[sl]=Integracija terminala GenericName[sv]=Integrering av terminal GenericName[tr]=Uçbirim Bütünleşmesi GenericName[uk]=Інтеграція термінала GenericName[x-test]=xxTerminal Integrationxx GenericName[zh_CN]=终端整合 GenericName[zh_TW]=終端機整合 Icon=utilities-terminal ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevkonsoleview X-KDE-PluginInfo-Name=kdevkonsoleview X-KDE-PluginInfo-Category=Utilities X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Mode=GUI diff --git a/plugins/openwith/kdevopenwith.desktop b/plugins/openwith/kdevopenwith.desktop index c665cf8353..2e2686fd0a 100644 --- a/plugins/openwith/kdevopenwith.desktop +++ b/plugins/openwith/kdevopenwith.desktop @@ -1,92 +1,94 @@ [Desktop Entry] Type=Service Icon=document-open Exec=blubb Comment=This plugin allows to open files with associated external applications. Comment[bg]=Тази приставка позволява отваряне на файлове със съответни външни програми. Comment[ca]=Aquest connector permet obrir fitxers amb aplicacions externes associades. Comment[ca@valencia]=Este connector permet obrir fitxers amb aplicacions externes associades. Comment[da]=Dette plugin tillader at åbne filer med associerede eksterne applikationer. Comment[de]=Mit diesem Modul können Dateien mit ihnen zugewiesenen externen Anwendungen gestartet werden. Comment[en_GB]=This plugin allows to open files with associated external applications. Comment[es]=Este complemento permite abrir archivos con aplicaciones externas asociadas. Comment[et]=See plugin võimaldab avada väliste rakendustega seostatud faile. Comment[fr]=Ce module externe permet d'ouvrir des fichiers avec des applications externes associées. Comment[gl]=Este engadido permite abrir ficheiros cos programas externos asociados. Comment[it]=Questa estensione permette di aprire i file con le relative applicazioni esterne. Comment[nb]=Dette programtillegget kan åpne filer med tilknyttede eksterne programmer. Comment[nds]=Mit dit Moduul laat sik Dateien mit towiest extern Programmen opmaken. Comment[nl]=Deze plugin biedt het openen van bestanden met geassocieerde externe toepassingen. Comment[pl]=Ta wtyczka pozwala na otwieranie plików, z którymi są powiązane zewnętrzne programy. Comment[pt]=Este 'plugin' permite abrir os ficheiros com as aplicações externas associadas. Comment[pt_BR]=Este plugin permite abrir os arquivos com os aplicativos externos associados. Comment[sl]=Vstavek omogoča odpiranje datotek v povezanih zunanjih programih. Comment[sv]=Insticksprogrammet tillåter att öppna filer med tillhörande externa program. Comment[uk]=За допомогою цього додатка ви зможете відкривати файли у пов’язаній з ними зовнішній програмі. Comment[x-test]=xxThis plugin allows to open files with associated external applications.xx Comment[zh_CN]=此插件允许使用关联的外部应用程序打开文件。 Comment[zh_TW]=此外掛程式讓您可以用外部應用程式開啟檔案。 Name=Open With Name[bg]=Отваряне с Name[ca]=Obre amb Name[ca@valencia]=Obri amb Name[cs]=Otevřít pomocí Name[da]=Åbn med Name[de]=Öffnen mit Name[en_GB]=Open With Name[es]=Abrir con Name[et]=Avamine rakendusega Name[fr]=Ouvrir avec Name[gl]=Abrir con Name[it]=Apri con Name[ja]=アプリケーションで開く Name[nb]=Åpne med Name[nds]=Opmaken mit Name[nl]=Openen met Name[pl]=Otwórz za pomocą Name[pt]=Abrir Com Name[pt_BR]=Abrir com +Name[ru]=Открыть с помощью Name[sl]=Odpri v Name[sv]=Öppna med Name[tr]=Birlikte Aç Name[uk]=Відкриття у зовнішніх програмах Name[x-test]=xxOpen Withxx Name[zh_CN]=打开方式 Name[zh_TW]=開啟方式 GenericName=Open With GenericName[bg]=Отваряне с GenericName[ca]=Obre amb GenericName[ca@valencia]=Obri amb GenericName[cs]=Otevřít pomocí GenericName[da]=Åbn med GenericName[de]=Öffnen mit GenericName[en_GB]=Open With GenericName[es]=Abrir con GenericName[et]=Avamine rakendusega GenericName[fr]=Ouvrir avec GenericName[gl]=Abrir con GenericName[it]=Apri con GenericName[ja]=アプリケーションで開く GenericName[nb]=Åpne med GenericName[nds]=Opmaken mit GenericName[nl]=Openen met GenericName[pl]=Otwórz za pomocą GenericName[pt]=Abrir Com GenericName[pt_BR]=Abrir com +GenericName[ru]=Открыть с помощью GenericName[sl]=Odpri v GenericName[sv]=Öppna med GenericName[tr]=Birlikte Aç GenericName[uk]=Відкриття у зовнішніх програмах GenericName[x-test]=xxOpen Withxx GenericName[zh_CN]=打开方式 GenericName[zh_TW]=開啟方式 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevopenwith X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDE-PluginInfo-Name=kdevopenwith X-KDE-PluginInfo-Author=Andreas Pakulat X-KDevelop-Interfaces=org.kdevelop.IOpenWith X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Core X-KDevelop-Mode=GUI diff --git a/plugins/patchreview/kdevpatchreview.desktop b/plugins/patchreview/kdevpatchreview.desktop index 47c03c4723..5176dcfd8d 100644 --- a/plugins/patchreview/kdevpatchreview.desktop +++ b/plugins/patchreview/kdevpatchreview.desktop @@ -1,90 +1,92 @@ [Desktop Entry] Type=Service Icon=applications-engineering Exec=blubb Comment=This plugin allows reviewing patches directly in the editor. Comment[bg]=Тази приставка позволява преглеждане на кръпки направо в редактора. Comment[ca]=Aquest connector permet revisar pedaços directament en l'editor. Comment[ca@valencia]=Este connector permet revisar pedaços directament en l'editor. Comment[da]=Dette plugin tillader at gennemgå rettelser direkte i editoren. Comment[de]=Dieses Modul ermöglich es, Patches direkt im Editor durchzusehen. Comment[en_GB]=This plugin allows reviewing patches directly in the editor. Comment[es]=Este complemento permite revisar parches directamente en el editor. Comment[et]=See plugin võimaldab paiku üle vaadata otse redaktoris. Comment[fr]=Ce module externe permet de réviser des correctifs directement dans l'éditeur. Comment[gl]=Este engadido permite revisar parches directamente no editor. Comment[it]=Questa estensione permette di rivedere le patch direttamente nell'editor. Comment[nb]=Dette programtillegget kan gjennomgå programlapper direkte i redigeringen. Comment[nds]=Mit dit Moduul laat sik Kodeplasters direktemang binnen den Editor nakieken. Comment[nl]=Deze plugin laat patches herzien direct in de editor. Comment[pl]=Ta wtyczka pozwala na przeglądanie poprawek bezpośrednio w edytorze. Comment[pt]=Este 'plugin' permite obter as modificações directamente no editor. Comment[pt_BR]=Este plugin permite obter as modificações diretamente no editor. Comment[sl]=Vstavek omogoča pregledovanje popravkov neposredno iz urejevalnika. Comment[sv]=Insticksprogrammet gör det möjligt att direkt granska programfixar i editorn. Comment[uk]=За допомогою цього додатка ви зможете рецензувати латки безпосередньо у редакторі. Comment[x-test]=xxThis plugin allows reviewing patches directly in the editor.xx Comment[zh_CN]=此插件允许在编辑器中直接审阅补丁。 Comment[zh_TW]=此外掛程式任您可以直接在編輯器裡檢視修補。 Name=Patch Review Name[bg]=Преглед на кръпки Name[ca]=Revisa pedaç Name[ca@valencia]=Revisa pedaç Name[da]=Gennemgang af rettelser Name[de]=Patch-Durchsicht Name[en_GB]=Patch Review Name[es]=Revisión de parches Name[et]=Paikade ülevaatamine Name[fr]=Révision de correctifs Name[gl]=Revisor de parches Name[it]=Revisione patch Name[ja]=パッチレビュー Name[nb]=Lappegjennomgang Name[nds]=Plasternakiek Name[nl]=Patchoverzicht Name[pl]=Przeglądanie poprawek Name[pt]=Revisão da Modificação Name[pt_BR]=Revisão da Modificação +Name[ru]=Рецензирование заплаток Name[sl]=Pregledovanje popravkov Name[sv]=Granska programfixar Name[tr]=Yama Gözden Geçirmesi Name[uk]=Рецензування латки Name[x-test]=xxPatch Reviewxx Name[zh_CN]=补丁审阅 Name[zh_TW]=修補檢視 GenericName=Patch Review GenericName[bg]=Преглед на кръпки GenericName[ca]=Revisa pedaç GenericName[ca@valencia]=Revisa pedaç GenericName[da]=Gennemgang af rettelser GenericName[de]=Patch-Durchsicht GenericName[en_GB]=Patch Review GenericName[es]=Revisión de parches GenericName[et]=Paikade ülevaatamine GenericName[fr]=Révision de correctifs GenericName[gl]=Revisor de parches GenericName[it]=Revisione patch GenericName[ja]=パッチレビュー GenericName[nb]=Lappegjennomgang GenericName[nds]=Plasternakiek GenericName[nl]=Patchoverzicht GenericName[pl]=Przeglądanie poprawek GenericName[pt]=Revisão da Modificação GenericName[pt_BR]=Revisão da Modificação +GenericName[ru]=Рецензирование заплаток GenericName[sl]=Pregledovanje popravkov GenericName[sv]=Granska programfixar GenericName[tr]=Yama Gözden Geçirmesi GenericName[uk]=Рецензування латки GenericName[x-test]=xxPatch Reviewxx GenericName[zh_CN]=补丁审阅 GenericName[zh_TW]=修補檢視 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevpatchreview X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Interfaces=org.kdevelop.IPatchReview X-KDE-PluginInfo-Name=kdevpatchreview X-KDE-PluginInfo-Author=David Nolden X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Utilities X-KDevelop-Mode=GUI diff --git a/plugins/problemreporter/kdevproblemreporter.desktop b/plugins/problemreporter/kdevproblemreporter.desktop index d03b85ef89..550a0a1680 100644 --- a/plugins/problemreporter/kdevproblemreporter.desktop +++ b/plugins/problemreporter/kdevproblemreporter.desktop @@ -1,89 +1,90 @@ [Desktop Entry] Type=Service Icon=emblem-important Exec=blubb Comment=This plugin shows errors in source code. Comment[bg]=Тази приставка показва грешки в изходния код. Comment[ca]=Aquest connector permet mostrar errors en el codi font. Comment[ca@valencia]=Este connector permet mostrar errors en el codi font. Comment[da]=Dette plugin viser fejl i kildekoden. Comment[de]=Dieses Modul zeigt Fehler im Quelltext an. Comment[en_GB]=This plugin shows errors in source code. Comment[es]=Este complemento muestra errores en el código fuente. Comment[et]=See plugin näitab vigu lähtekoodis. Comment[fr]=Ce module externe affiche les erreurs dans le code source. Comment[gl]=Esta extensión mostra erros no código fonte. Comment[it]=Questa estensione mostra gli errori nel codice sorgente. Comment[nb]=Dette programtillegget viser feil i kildekode. Comment[nds]=Dit Moduul wiest Fehlers binnen Bornkode. Comment[nl]=Deze plugin toont fouten in broncode Comment[pl]=Ta wtyczka pokazuje błędy w kodzie źródłowym. Comment[pt]=Este 'plugin' apresenta os erros no código-fonte. Comment[pt_BR]=Este plugin apresenta os erros no código-fonte. Comment[sl]=Vstavek prikazuje napake v izvorni kodi. Comment[sv]=Insticksprogrammet visar fel i källkod. Comment[uk]=За допомогою цього додатка можна переглянути повідомлення про помилки у коді. Comment[x-test]=xxThis plugin shows errors in source code.xx Comment[zh_CN]=此插件显示源代码中的错误。 Comment[zh_TW]=此外掛程式顯示源碼中的錯誤。 Name=Problem Reporter View Name[bg]=Преглед на съобщенията за грешки Name[ca]=Vista del notificador de problemes Name[ca@valencia]=Vista del notificador de problemes Name[da]=Visning af problemrapportering Name[de]=Ansicht für Fehlerberichte Name[en_GB]=Problem Reporter View Name[es]=Vista del notificador de problemas Name[et]=Probleemide teavitaja vaade Name[fr]=Vue du rapporteur de problèmes Name[gl]=Vista do relator de problemas Name[it]=Vista segnalazione problemi Name[ja]=問題レポータービュー Name[nb]=Problemmeldervisning Name[nds]=Problemsöök-Ansicht Name[nl]=Probleemrapporteuroverzicht Name[pl]=Widok raportów o problemach Name[pt]=Área de Relatórios de Erros Name[pt_BR]=Área de Relatórios de Erros Name[sl]=Prikaz poročevalca o težavah Name[sv]=Problemrapporteringsvy Name[tr]=Sorun Bildirici Görünümü Name[uk]=Перегляд інструменту звітування про проблеми Name[x-test]=xxProblem Reporter Viewxx Name[zh_CN]=错误汇报视图 Name[zh_TW]=問題回報器檢視 GenericName=Problem Reporter GenericName[bg]=Съобщения за грешки GenericName[ca]=Notificador de problemes GenericName[ca@valencia]=Notificador de problemes GenericName[da]=Problemrapportering GenericName[de]=Fehlerberichte GenericName[en_GB]=Problem Reporter GenericName[es]=Notificador de problemas GenericName[et]=Probleemide teavitaja GenericName[fr]=Rapporteur de problèmes GenericName[gl]=Relator de problemas GenericName[it]=Segnalazione problemi GenericName[ja]=問題レポーター GenericName[nb]=Problemmelder GenericName[nds]=Problemsöök GenericName[nl]=Probleemrapporteur GenericName[pl]=Raporty o problemach GenericName[pt]=Relato de Problemas GenericName[pt_BR]=Relato de Problemas +GenericName[ru]=Проблемы GenericName[sl]=Poročevalec o težavah GenericName[sv]=Problemrapportör GenericName[tr]=Sorun Bildirici GenericName[uk]=Сповіщення про проблеми GenericName[x-test]=xxProblem Reporterxx GenericName[zh_CN]=错误汇报 GenericName[zh_TW]=問題回報器 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevproblemreporter X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDE-PluginInfo-Name=kdevproblemreporter X-KDE-PluginInfo-Author=Hamish Rodda X-KDE-PluginInfo-License=LGPL X-KDE-PluginInfo-Category=Utilities X-KDevelop-Mode=GUI diff --git a/plugins/problemreporter/problemmodel.cpp b/plugins/problemreporter/problemmodel.cpp index e0c5a3b2e5..dd5f63167f 100644 --- a/plugins/problemreporter/problemmodel.cpp +++ b/plugins/problemreporter/problemmodel.cpp @@ -1,397 +1,397 @@ /* * KDevelop Problem Reporter * * Copyright 2007 Hamish Rodda * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "problemreporterplugin.h" #include "watcheddocumentset.h" using namespace KDevelop; ProblemModel::ProblemModel(ProblemReporterPlugin * parent) : QAbstractItemModel(parent), m_plugin(parent), m_lock(QReadWriteLock::Recursive), m_showImports(false), m_severity(ProblemData::Hint), m_documentSet(0) { m_minTimer = new QTimer(this); m_minTimer->setInterval(MinTimeout); m_minTimer->setSingleShot(true); connect(m_minTimer, SIGNAL(timeout()), SLOT(timerExpired())); m_maxTimer = new QTimer(this); m_maxTimer->setInterval(MaxTimeout); m_maxTimer->setSingleShot(true); connect(m_maxTimer, SIGNAL(timeout()), SLOT(timerExpired())); setScope(CurrentDocument); connect(ICore::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), SLOT(setCurrentDocument(KDevelop::IDocument*))); // CompletionSettings include a list of todo markers we care for, so need to update connect(ICore::self()->languageController()->completionSettings(), SIGNAL(settingsChanged(ICompletionSettings*)), SLOT(forceFullUpdate())); if (ICore::self()->documentController()->activeDocument()) { setCurrentDocument(ICore::self()->documentController()->activeDocument()); } } const int ProblemModel::MinTimeout = 1000; const int ProblemModel::MaxTimeout = 5000; ProblemModel::~ ProblemModel() { m_problems.clear(); } int ProblemModel::rowCount(const QModelIndex & parent) const { if (!parent.isValid()) return m_problems.count(); if (parent.internalId() && parent.column() == 0) return m_problems.at(parent.row())->locationStack().count(); return 0; } QString getDisplayUrl(const QString &url, const KUrl &base) { KUrl location(url); QString displayedUrl; if ( location.protocol() == base.protocol() && location.user() == base.user() && location.host() == base.host() ) { bool isParent; displayedUrl = KUrl::relativePath(base.path(), location.path(), &isParent ); if ( !isParent ) { displayedUrl = location.pathOrUrl(); } } else { displayedUrl = location.pathOrUrl(); } return displayedUrl; } QVariant ProblemModel::data(const QModelIndex & index, int role) const { if (!index.isValid()) return QVariant(); // Locking the duchain here leads to a deadlock, because kate triggers some paint to the outside while holding the smart-lock // DUChainReadLocker lock(DUChain::lock()); ProblemPointer p = problemForIndex(index); KUrl baseDirectory = m_currentDocument.upUrl(); if (!index.internalId()) { // Top level switch (role) { case Qt::DisplayRole: switch (index.column()) { case Source: return p->sourceString(); break; case Error: return p->description(); case File: { return getDisplayUrl(p->finalLocation().document.str(), baseDirectory); } case Line: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.line + 1); break; case Column: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.column + 1); break; } break; case Qt::ToolTipRole: return p->explanation(); default: break; } } else { switch (role) { case Qt::DisplayRole: switch (index.column()) { case Error: return i18n("In file included from:"); case File: { return getDisplayUrl(p->locationStack().at(index.row()).document.str(), baseDirectory); } case Line: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.line + 1); break; case Column: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.column + 1); break; } break; default: break; } } return QVariant(); } QModelIndex ProblemModel::parent(const QModelIndex & index) const { if (index.internalId()) return createIndex(m_problems.indexOf(problemForIndex(index)), 0, 0); return QModelIndex(); } QModelIndex ProblemModel::index(int row, int column, const QModelIndex & parent) const { DUChainReadLocker lock(DUChain::lock()); if (row < 0 || column < 0 || column >= LastColumn) return QModelIndex(); if (parent.isValid()) { if (parent.internalId()) return QModelIndex(); if (parent.column() != 0) return QModelIndex(); ProblemPointer problem = problemForIndex(parent); if (row >= problem->locationStack().count()) return QModelIndex(); ///@todo Make location-stack work again return createIndex(row, column, row); } if (row < m_problems.count()) return createIndex(row, column, 0); return QModelIndex(); } int ProblemModel::columnCount(const QModelIndex & parent) const { Q_UNUSED(parent) return LastColumn; } KDevelop::ProblemPointer ProblemModel::problemForIndex(const QModelIndex & index) const { if (index.internalId()) return m_problems.at(index.internalId()); else return m_problems.at(index.row()); } ProblemReporterPlugin* ProblemModel::plugin() { return m_plugin; } QVariant ProblemModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); if (role != Qt::DisplayRole) return QVariant(); switch (section) { case Source: - return i18n("Source"); + return i18nc("@title:column source of problem", "Source"); case Error: - return i18n("Problem"); + return i18nc("@title:column problem description", "Problem"); case File: - return i18n("File"); + return i18nc("@title:column file where problem was found", "File"); case Line: - return i18n("Line"); + return i18nc("@title:column line number with problem", "Line"); case Column: - return i18n("Column"); + return i18nc("@title:column column number with problem", "Column"); } return QVariant(); } void ProblemModel::problemsUpdated(const KDevelop::IndexedString& url) { QReadLocker locker(&m_lock); if (m_documentSet->get().contains(url)) { // m_minTimer will expire in MinTimeout unless some other parsing job finishes in this period. m_minTimer->start(); // m_maxTimer will expire unconditionally in MaxTimeout if (!m_maxTimer->isActive()) { m_maxTimer->start(); } } } void ProblemModel::timerExpired() { m_minTimer->stop(); m_maxTimer->stop(); rebuildProblemList(); } QList ProblemModel::getProblems(IndexedString url, bool showImports) { QList result; QSet visitedContexts; DUChainReadLocker lock; getProblemsInternal(DUChain::self()->chainForDocument(url), showImports, visitedContexts, result); return result; } QList< ProblemPointer > ProblemModel::getProblems(QSet< IndexedString > urls, bool showImports) { QList result; QSet visitedContexts; DUChainReadLocker lock; foreach(const IndexedString& url, urls) { getProblemsInternal(DUChain::self()->chainForDocument(url), showImports, visitedContexts, result); } return result; } void ProblemModel::getProblemsInternal(TopDUContext* context, bool showImports, QSet& visitedContexts, QList& result) { if (!context || visitedContexts.contains(context)) { return; } foreach(ProblemPointer p, context->problems()) { if (p->severity() <= m_severity) { result.append(p); } } visitedContexts.insert(context); if (showImports) { bool isProxy = context->parsingEnvironmentFile() && context->parsingEnvironmentFile()->isProxyContext(); foreach(const DUContext::Import &ctx, context->importedParentContexts()) { if(!ctx.indexedContext().indexedTopContext().isLoaded()) continue; TopDUContext* topCtx = dynamic_cast(ctx.context(0)); if(topCtx) { //If we are starting at a proxy-context, only recurse into other proxy-contexts, //because those contain the problems. if(!isProxy || (topCtx->parsingEnvironmentFile() && topCtx->parsingEnvironmentFile()->isProxyContext())) getProblemsInternal(topCtx, showImports, visitedContexts, result); } } } } void ProblemModel::rebuildProblemList() { // No locking here, because it may be called from an already locked context m_problems = getProblems(m_documentSet->get(), m_showImports); reset(); } void ProblemModel::setCurrentDocument(KDevelop::IDocument* document) { QWriteLocker locker(&m_lock); m_currentDocument = document->url(); m_documentSet->setCurrentDocument(IndexedString(m_currentDocument)); reset(); } void ProblemModel::setShowImports(bool showImports) { if (m_showImports != showImports) { QWriteLocker locker(&m_lock); m_showImports = showImports; rebuildProblemList(); } } void ProblemModel::setScope(int scope) { Scope cast_scope = static_cast(scope); QWriteLocker locker(&m_lock); if (!m_documentSet || m_documentSet->getScope() != cast_scope) { if (m_documentSet) { delete m_documentSet; } switch (cast_scope) { case CurrentDocument: m_documentSet = new CurrentDocumentSet(IndexedString(m_currentDocument), this); break; case OpenDocuments: m_documentSet = new OpenDocumentSet(this); break; case CurrentProject: m_documentSet = new CurrentProjectSet(IndexedString(m_currentDocument), this); break; case AllProjects: m_documentSet = new AllProjectSet(this); break; } connect(m_documentSet, SIGNAL(changed()), this, SLOT(documentSetChanged())); rebuildProblemList(); } } void ProblemModel::setSeverity(int severity) { ProblemData::Severity cast_severity = static_cast(severity); if (m_severity != cast_severity) { QWriteLocker locker(&m_lock); m_severity = cast_severity; rebuildProblemList(); } } void ProblemModel::documentSetChanged() { rebuildProblemList(); } void ProblemModel::forceFullUpdate() { m_lock.lockForRead(); QSet documents = m_documentSet->get(); m_lock.unlock(); DUChainReadLocker lock(DUChain::lock()); foreach(const IndexedString& document, documents) { if (document.isEmpty()) continue; TopDUContext::Features updateType = TopDUContext::ForceUpdate; if(documents.size() == 1) updateType = TopDUContext::ForceUpdateRecursive; DUChain::self()->updateContextForUrl(document, (TopDUContext::Features)(updateType | TopDUContext::VisibleDeclarationsAndContexts)); } } diff --git a/plugins/quickopen/kdevquickopen.desktop b/plugins/quickopen/kdevquickopen.desktop index 5b26f87dd4..768e8f5b49 100644 --- a/plugins/quickopen/kdevquickopen.desktop +++ b/plugins/quickopen/kdevquickopen.desktop @@ -1,98 +1,98 @@ [Desktop Entry] Type=Service Icon=quickopen Exec=blubb Comment=This plugin allows quick access to project files and language-items like classes/functions. Comment[bg]=Тази приставка позволява бърз достъп до файловете на проекта и езиковите средства класове/уфнкции. Comment[ca]=Aquest connector permet un ràpid accés als fitxers del projecte i a elements del llenguatge com classes/funcions. Comment[ca@valencia]=Este connector permet un ràpid accés als fitxers del projecte i a elements del llenguatge com classes/funcions. Comment[da]=Dette plugin tillader hurtig adgang til projektfiler og sprogelementer som klasser/funktioner. Comment[de]=Dieses Modul bietet schnellen Zugriff auf Projektdateien und Spachelemente wie Klassen und Funktionen. Comment[el]=Το πρόσθετο αυτό επιτρέπει τη γρήγορη πρόσβαση σε αρχεία έργου και αντικείμενα γλώσσας όπως κλάσεις/συναρτήσεις. Comment[en_GB]=This plugin allows quick access to project files and language-items like classes/functions. Comment[es]=Este complemento permite un rápido acceso a los archivos del proyecto y a elementos del lenguaje, como clases y funciones. Comment[et]=See plugin pakub kiiret ligipääsu projekti failidele ja keele elementidele, näiteks klassidele ja funktsioonidele. Comment[fr]=Ce module externe permet un accès rapide aux fichiers de projets et aux éléments de langage comme les classes/fonctions. Comment[gl]=Este engadido permite acceder rapidamente a ficheiros de proxecto e elementos da linguaxe como clases e funcións. Comment[it]=Questa estensione permette di accedere rapidamente ai file di progetto e agli elementi del linguaggio come le classi/funzioni. Comment[nb]=Dette programtillegget gir rask tilgang til prosjektfiler og språkelementer som klasser og funksjoner. Comment[nds]=Dit Moduul stellt Fixtogriep op Projektdateien un Spraakelementen as Klassen un Funkschonen praat. Comment[nl]=Deze plugin biedt snel toegamg tot projectbestanden en taal-items zoals classes/functies. Comment[pl]=Ta wtyczka pozwala na szybki dostęp do plików projektu i elementów języka takich jak klasy i funkcje. Comment[pt]=Este 'plugin' permite um acesso rápido aos ficheiros do projecto e aos itens das linguagens, como as classes/funções. Comment[pt_BR]=Esta extensão permite o rápido acesso a arquivos de projeto e itens da linguagem como classes/funções. -Comment[ru]=Этот модуль позволяет получать быстрый доступ к файлам проекта и языковым объектам, таким как классы и функции. +Comment[ru]=Это расширение позволяет получать быстрый доступ к файлам проекта и языковым объектам, таким как классы и функции. Comment[sl]=Vstavek omogoča hiter dostop do projektnih datotek in struktur kot so razredi in funkcije. Comment[sv]=Insticksprogrammet ger snabb åtkomst av projektfiler och språkobjekt som klasser och funktioner. Comment[uk]=За допомогою цього додатка можна пришвидшити доступ до файлів проектів і елементів мови на зразок класів або функцій. Comment[x-test]=xxThis plugin allows quick access to project files and language-items like classes/functions.xx Comment[zh_CN]=此插件允许快速访问工程文件和诸如类/函数的语言项目。 Comment[zh_TW]=此外掛程式讓您快速存取專案檔案與一些語言的項目,如類別或函式等。 Name=Quick Open Name[bg]=Бързо отваряне Name[ca]=Obertura ràpida Name[ca@valencia]=Obertura ràpida Name[cs]=Rychle otevřít Name[da]=Åbn hurtigt Name[de]=Schnellöffner Name[en_GB]=Quick Open Name[es]=Apertura rápida Name[et]=Kiiravamine Name[fr]=Ouverture rapide Name[ga]=Oscailt Thapa Name[gl]=Apertura rápida Name[it]=Apertura veloce Name[ja]=クイックオープン Name[nb]=Hurtigåpne Name[nds]=Fixopmaken Name[nl]=Snel openen Name[pl]=Szybkie otwarcie Name[pt]=Abertura Rápida Name[pt_BR]=Abrir rapidamente Name[ru]=Быстрый доступ Name[sl]=Hitro odpiranje Name[sv]=Snabböppna Name[tr]=Hızlı Aç Name[uk]=Швидке відкриття Name[x-test]=xxQuick Openxx Name[zh_CN]=快速打开 Name[zh_TW]=快速開啟 GenericName=Quick Open GenericName[bg]=Бързо отваряне GenericName[ca]=Obertura ràpida GenericName[ca@valencia]=Obertura ràpida GenericName[cs]=Rychle otevřít GenericName[da]=Åbn hurtigt GenericName[de]=Schnellöffner GenericName[en_GB]=Quick Open GenericName[es]=Apertura rápida GenericName[et]=Kiiravamine GenericName[fr]=Ouverture rapide GenericName[ga]=Oscailt Thapa GenericName[gl]=Apertura rápida GenericName[it]=Apertura veloce GenericName[ja]=クイックオープン GenericName[nb]=Hurtigåpne GenericName[nds]=Fixopmaken GenericName[nl]=Snel openen GenericName[pl]=Szybkie otwarcie GenericName[pt]=Abertura Rápida GenericName[pt_BR]=Abrir rapidamente GenericName[ru]=Быстрый доступ GenericName[sl]=Hitro odpiranje GenericName[sv]=Snabböppna GenericName[tr]=Hızlı Aç GenericName[uk]=Швидке відкриття GenericName[x-test]=xxQuick Openxx GenericName[zh_CN]=快速打开 GenericName[zh_TW]=快速開啟 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevquickopen X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Interfaces=org.kdevelop.IQuickOpen X-KDE-PluginInfo-Name=kdevquickopen X-KDE-PluginInfo-Author=David Nolden X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Core X-KDevelop-Mode=GUI diff --git a/plugins/reviewboard/kdevreviewboard.desktop b/plugins/reviewboard/kdevreviewboard.desktop index 7f5b96f923..6c1eac0683 100644 --- a/plugins/reviewboard/kdevreviewboard.desktop +++ b/plugins/reviewboard/kdevreviewboard.desktop @@ -1,77 +1,79 @@ [Desktop Entry] Type=Service Icon=reviewboard Exec=blubb Comment=ReviewBoard integration for KDevelop Comment[ca]=Integració de ReviewBoard pel KDevelop Comment[ca@valencia]=Integració de ReviewBoard pel KDevelop Comment[da]=Integration af ReviewBoard med KDevelop Comment[de]=ReviewBoard-Integration für KDevelop Comment[en_GB]=ReviewBoard integration for KDevelop Comment[es]=Integración de ReviewBoard en KDevelop Comment[et]=ReviewBoardi lõimimine KDevelopiga Comment[fr]=Intégration de ReviewBoard pour KDevelop Comment[it]=Integrazione ReviewBoard per KDevelop Comment[nb]=ReviewBoard-integrering for KDevelop Comment[nds]=ReviewBoard-Inbinnen för KDevelop Comment[nl]=ReviewBoard-integratie voor KDevelop Comment[pt]=Integração do ReviewBoard para o KDevelop Comment[pt_BR]=Integração do ReviewBoard para o KDevelop Comment[sl]=Integracija orodja ReviewBoard v KDevelop Comment[sv]=Integrering av Reviewboard i KDevelop Comment[uk]=Інтеграція ReviewBoard з KDevelop Comment[x-test]=xxReviewBoard integration for KDevelopxx Comment[zh_CN]=KDevelop 的 ReviewBoard 集成 Comment[zh_TW]=ReviewBoard 與 KDevelop 整合 Name=ReviewBoard Name[ca]=ReviewBoard Name[ca@valencia]=ReviewBoard Name[da]=ReviewBoard Name[de]=ReviewBoard Name[en_GB]=ReviewBoard Name[es]=ReviewBoard Name[et]=ReviewBoard Name[fr]=ReviewBoard Name[it]=ReviewBoard Name[nb]=ReviewBoard Name[nds]=ReviewBoard Name[nl]=ReviewBoard Name[pt]=ReviewBoard Name[pt_BR]=ReviewBoard +Name[ru]=ReviewBoard Name[sl]=ReviewBoard Name[sv]=Reviewboard Name[uk]=ReviewBoard Name[x-test]=xxReviewBoardxx Name[zh_CN]=ReviewBoard Name[zh_TW]=ReviewBoard GenericName=ReviewBoard GenericName[ca]=ReviewBoard GenericName[ca@valencia]=ReviewBoard GenericName[da]=ReviewBoard GenericName[de]=ReviewBoard GenericName[en_GB]=ReviewBoard GenericName[es]=ReviewBoard GenericName[et]=ReviewBoard GenericName[fr]=ReviewBoard GenericName[it]=ReviewBoard GenericName[nb]=ReviewBoard GenericName[nds]=ReviewBoard GenericName[nl]=ReviewBoard GenericName[pt]=ReviewBoard GenericName[pt_BR]=ReviewBoard +GenericName[ru]=ReviewBoard GenericName[sl]=ReviewBoard GenericName[sv]=Reviewboard GenericName[uk]=ReviewBoard GenericName[x-test]=xxReviewBoardxx GenericName[zh_CN]=ReviewBoard GenericName[zh_TW]=ReviewBoard ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevreviewboard X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDE-PluginInfo-Name=kdevreviewboard X-KDE-PluginInfo-Author=Aleix Pol X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Utilities X-KDevelop-Mode=GUI X-KDevelop-Interfaces=org.kdevelop.IPatchExporter diff --git a/plugins/snippet/kdevsnippet.desktop b/plugins/snippet/kdevsnippet.desktop index c63736ca25..e2fd7f04ce 100644 --- a/plugins/snippet/kdevsnippet.desktop +++ b/plugins/snippet/kdevsnippet.desktop @@ -1,94 +1,94 @@ [Desktop Entry] Type=Service Exec=blubb Icon=edit-copy Comment=This plugin allows to store code snippets and insert them into open files Comment[bg]=Тази приставка позволява съхраняването на временни фрагменти от програмни низове и вмъкването им в отворени файлове Comment[ca]=Aquest connector permet desar retalls de codi i inserir-los a fitxers oberts Comment[ca@valencia]=Este connector permet alçar retalls de codi i inserir-los a fitxers oberts Comment[da]=Dette plugin tillader at gemme kodestumper og indsætte dem i åbne filer Comment[de]=Dieses Modul bietet die Möglichkeit, Textbausteine zu speichern und in geöffnete Dateien einzufügen. Comment[el]=Αυτό το πρόσθετο σας επιτρέπει να αποθηκεύσετε δείγματα κώδικα και να τα εισάγετε σε ανοιχτά αρχεία Comment[en_GB]=This plugin allows to store code snippets and insert them into open files Comment[es]=Este complemento le permite almacenar fragmentos de código e insertarlos en los archivos abiertos Comment[et]=See plugin võimaldab salvestada koodijuppe ja lisada neid avatud failidesse Comment[fr]=Ce module externe permet d'enregistrer des fragments de code et de les insérer dans les fichiers ouverts Comment[gl]=Este engadido permite gardar fragmentos de código e inserilos en ficheiros abertos Comment[it]=Questa estensione permette di memorizzare i frammenti di codice e inserirli in file aperti Comment[nb]=Dette programtillegget kan lagre kodebiter og sette dem inn i åpne filer Comment[nds]=Dit Moduul wohrt Kodesnippels un föögt se na opmaakt Dateien in Comment[nl]=Deze plugin biedt het opslaan van codeknipsels en voegt ze in in open bestanden Comment[pl]=Ta wtyczka umożliwia zapisanie fragmentów kodu i wstawianie ich później otwartych plików Comment[pt]=Este 'plugin' permite-lhe guardar excertos de código e inseri-los nos ficheiros abertos Comment[pt_BR]=Esta extensão permite armazenar trechos de código e inseri-los em arquivos abertos -Comment[ru]=Этот модуль позволяет хранить фрагменты кода и вставлять их в открытые файлы. +Comment[ru]=Это расширение позволяет хранить фрагменты кода и вставлять их в открытые файлы. Comment[sl]=Vstavek omogoča shranjevanje delčkov kode in vstavljanje delčkov v odprte datoteke Comment[sv]=Insticksprogrammet gör det möjligt att lagra kodsnuttar och infoga dem i öppna filer Comment[uk]=За допомогою цього додатка можна зберігати фрагменти коду і вставляти їх до відкритих файлів Comment[x-test]=xxThis plugin allows to store code snippets and insert them into open filesxx Comment[zh_CN]=此插件允许存储代码片断并插入到打开的文件中去 Comment[zh_TW]=此外掛程式允許儲存程式碼片段,並將它們插入開啟的檔案中 Name=Code Snippets Support Name[bg]=Поддръжка на отрязъци от код Name[ca]=Implementació de retalls de codi Name[ca@valencia]=Implementació de retalls de codi Name[da]=Understøttelse af kodestumper Name[de]=Unterstützung für Textbausteine Name[el]=Υποστήριξη δειγμάτων κώδικα Name[en_GB]=Code Snippets Support Name[es]=Implementación de fragmentos de código Name[et]=Koodijuppide toetus Name[fr]=Prise en charge des fragments de code Name[gl]=Soporte de fragmentos de código Name[it]=Supporto frammenti di codice Name[ja]=コードスニペットのサポート Name[nb]=Støtte for kodebiter Name[nds]=Kodesnippels-Ünnerstütten Name[nl]=Ondersteuning voor codeknipsels Name[pa]=ਕੋਡ ਸਨਿੱਪਟ ਸਹਿਯੋਗ Name[pl]=Obsługa fragmentów kodu Name[pt]=Suporte para Excertos de Código Name[pt_BR]=Suporte a trechos de código Name[sl]=Podpora za delčke kode Name[sv]=Stöd för kodsnuttar Name[tr]=Kod Parçacıkları Desteği Name[uk]=Підтримка фрагментів коду Name[x-test]=xxCode Snippets Supportxx Name[zh_CN]=代码片断支持 Name[zh_TW]=程式碼片段支援 GenericName=Code Snippets Support GenericName[bg]=Поддръжка на отрязъци от код GenericName[ca]=Implementació de retalls de codi GenericName[ca@valencia]=Implementació de retalls de codi GenericName[da]=Understøttelse af kodestumper GenericName[de]=Unterstützung für Textbausteine GenericName[en_GB]=Code Snippets Support GenericName[es]=Implementación de fragmentos de código GenericName[et]=Koodijuppide toetus GenericName[fr]=Prise en charge des fragments de code GenericName[gl]=Soporte de fragmentos de código GenericName[it]=Supporto frammenti di codice GenericName[ja]=コードスニペットのサポート GenericName[nb]=Støtte for kodebiter GenericName[nds]=Kodesnippels-Ünnerstütten GenericName[nl]=Ondersteuning voor codeknipsels GenericName[pl]=Obsługa fragmentów kodu GenericName[pt]=Suporte para Excertos de Código GenericName[pt_BR]=Suporte a trechos de código GenericName[sl]=Podpora za delčke kode GenericName[sv]=Stöd för kodsnuttar GenericName[tr]=Kod Parçacıkları Desteği GenericName[uk]=Підтримка фрагментів коду GenericName[x-test]=xxCode Snippets Supportxx GenericName[zh_CN]=代码片断支持 GenericName[zh_TW]=程式碼片段支援 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevsnippet X-KDE-PluginInfo-Name=kdevsnippet X-KDE-PluginInfo-Author=Robert Gruber, Milian Wolff X-KDE-PluginInfo-Version=0.75 X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Utilities X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Mode=GUI diff --git a/plugins/standardoutputview/kdevstandardoutputview.desktop b/plugins/standardoutputview/kdevstandardoutputview.desktop index b28a7c4a36..6e974135bf 100644 --- a/plugins/standardoutputview/kdevstandardoutputview.desktop +++ b/plugins/standardoutputview/kdevstandardoutputview.desktop @@ -1,83 +1,85 @@ [Desktop Entry] Type=Service Name=Output View Name[bg]=Преглед на резултата Name[ca]=Vista de la sortida Name[ca@valencia]=Vista de l'eixida Name[cs]=Pohled na výstup Name[da]=Visning af output Name[de]=Ansicht für Ausgaben Name[en_GB]=Output View Name[es]=Vista de la salida Name[et]=Väljundivaade Name[fr]=Vue de la sortie Name[ga]=Amharc Aschurtha Name[gl]=Vista da saída Name[it]=Vista output Name[ja]=出力ビュー Name[nb]=Utdata-visning Name[nds]=Utgaavansicht Name[nl]=Uitvoerweergave Name[pl]=Widok wyjścia Name[pt]=Área de Resultados Name[pt_BR]=Área de Resultados +Name[ru]=Просмотр вывода Name[sl]=Prikaz izhoda Name[sv]=Utmatningsvy Name[tr]=Çıktı Görünümü Name[uk]=Перегляд виводу Name[x-test]=xxOutput Viewxx Name[zh_CN]=输出视图 Name[zh_TW]=輸出檢視 GenericName=Output View GenericName[bg]=Преглед на резултата GenericName[ca]=Vista de la sortida GenericName[ca@valencia]=Vista de l'eixida GenericName[da]=Visning af output GenericName[de]=Ansicht für Ausgaben GenericName[en_GB]=Output View GenericName[es]=Vista de la salida GenericName[et]=Väljundivaade GenericName[fr]=Vue de la sortie GenericName[ga]=Amharc Aschurtha GenericName[it]=Vista output GenericName[ja]=出力ビュー GenericName[nb]=Utdata-visning GenericName[nds]=Utgaavansicht GenericName[nl]=Uitvoerweergave GenericName[pt]=Área de Resultados GenericName[pt_BR]=Área de Resultados +GenericName[ru]=Просмотр вывода GenericName[sl]=Prikaz izhoda GenericName[sv]=Utmatningsvy GenericName[uk]=Панель виводу даних GenericName[x-test]=xxOutput Viewxx GenericName[zh_CN]=输出视图 GenericName[zh_TW]=輸出檢視 Comment=Provides a text output toolview for other plugins to use, to show things like compiler messages. Comment[ca]=Proporciona una vista d'eina de sortida de text per a utilitzar en altres connectors, per a visualitzar missatges del compilador, per exemple. Comment[ca@valencia]=Proporciona una vista d'eina d'eixida de text per a utilitzar en altres connectors, per a visualitzar missatges del compilador, per exemple. Comment[da]=Giver en værktøjsvisning til tekst-output som andre plugins kan bruge til at vise ting som f.eks. meddelelser fra oversætteren. Comment[de]=Stellt eine Textausgabe für andere Module zur Verfügung, um Dinge wie Compiler-Nachrichten anzuzeigen. Comment[en_GB]=Provides a text output toolview for other plugins to use, to show things like compiler messages. Comment[es]=Proporciona una vista de salida de texto que pueden usar otros complementos para mostrar cosas como mensajes del compilador. Comment[et]=Teistele pluginatele kättesaadav tekstiväljundi tööriistavaade, mis näitab kompilaatori teateid ja muud. Comment[fr]=Fournit une vue des outils de sortie en mode texte pour à utiliser pour afficher des éléments comme les messages du compilateur. Comment[it]=Fornisce una vista strumenti testuale per le estensioni da usare, per mostrare le cose come i messaggi del compilatore. Comment[nb]=Viser en tekstlig verktøyvisning av utdata som andre programtillegg kan bruke, for å vise slikt som kompilatormeldinger. Comment[nds]=Stellt en Textutgaav för den Bruuk mit anner Modulen praat. Wiest Saken as a.B. Kompilerermellen. Comment[nl]=Levert tekstuitvoer van hulpmiddelen voor andere te gebruiken plugins, om zaken te tonen zoals berichten van compilers. Comment[pt]=Oferece uma área de resultados de texto para os outros 'plugins' usarem, de modo a mostrar coisas como as mensagens do compilador. Comment[pt_BR]=Oferece uma área de resultados de texto para os outros plugins usarem, de modo a mostrar coisas como as mensagens do compilador. Comment[sl]=Drugim vstavkom ponuja okno za prikaz besedilnega izhoda, na primer za sporočila razhroščevalnika. Comment[sv]=Tillhandahåller en verktygsvy för textutmatning som andra insticksprogram kan använda för att visa saker som kompilatormeddelanden. Comment[uk]=Забезпечує роботу панелі показу текстових даних інших додатків, зокрема попереджень компілятора. Comment[x-test]=xxProvides a text output toolview for other plugins to use, to show things like compiler messages.xx Comment[zh_CN]=提供让其它插件使用的文本输出工具视图,以便显示诸如编译器消息的信息。 Comment[zh_TW]=提供文字輸出工具檢視給其它外掛程式使用,顯示一些像是編譯器的訊息等等。 Icon=gear ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevstandardoutputview X-KDE-PluginInfo-Name=KDevStandardOutputView X-KDE-PluginInfo-Category=Core X-KDevelop-Version=11 X-KDevelop-Interfaces=org.kdevelop.IOutputView X-KDevelop-Mode=GUI diff --git a/plugins/subversion/kdevsubversion.desktop b/plugins/subversion/kdevsubversion.desktop index a1d0425aa5..73367595e0 100644 --- a/plugins/subversion/kdevsubversion.desktop +++ b/plugins/subversion/kdevsubversion.desktop @@ -1,102 +1,102 @@ [Desktop Entry] Type=Service Icon=svn_merge Exec=blubb Comment=This plugin integrates subversion to KDevelop. Comment[bg]=Тази приставка вгражда subversion в KDevelop. Comment[ca]=Aquest connector integra subversion en KDevelop. Comment[ca@valencia]=Este connector integra subversion en KDevelop. Comment[da]=Dette plugin integrerer subversion med KDevelop. Comment[de]=Dieses Modul integriert Subversion in KDevelop. Comment[el]=Αυτό το πρόσθετο ενσωματώνει το subversion στο KDevelop Comment[en_GB]=This plugin integrates subversion to KDevelop. Comment[es]=Este complemento integra «Subversion» en KDevelop. Comment[et]=See plugin lõimib Subversioni KDeveloppi. Comment[fr]=Ce module externe intègre Subversion à KDevelop. Comment[gl]=Este engadido integra subversion en KDevelop. Comment[it]=Questa estensione integra subversion in KDevelop. Comment[ja]=このプラグインは Subversion を KDevelop に統合します Comment[lv]=Šis spraudnis integrē SVN iekš KDevelop. Comment[nb]=Dette programtillegget integrerer subversion i KDevelop. Comment[nds]=Dit Moduul bett Subversion na KDevelop in Comment[nl]=Deze plugin integreert subversion in KDevelop. Comment[pl]=Ta wtyczka udostępnia Subversion w KDevelopie Comment[pt]=Este 'plugin' integra o Subversion com o KDevelop. Comment[pt_BR]=Esta extensão integra o Subversion ao KDevelop Comment[ro]=Acest modul integrează subversion în KDevelop -Comment[ru]=Этот модуль добавляет поддержку работы с Subversion в KDevelop +Comment[ru]=Это расширение добавляет поддержку работы с Subversion в KDevelop Comment[sl]=Vstavek v KDevelop integrira Subversion. Comment[sv]=Insticksprogrammet integrerar Subversion i KDevelop Comment[tr]=Bu eklenti Subversion ile KDevelop uygulamasını bütünleştirir Comment[uk]=За допомогою цього додатка можна інтегрувати subversion (SVN) до KDevelop. Comment[x-test]=xxThis plugin integrates subversion to KDevelop.xx Comment[zh_CN]=此插件对 KDevelop 整合 subversion。 Comment[zh_TW]=此外掛程式將 subversion 整合進 KDevelop。 Name=Subversion Support Name[bg]=Поддръжка на Subversion Name[ca]=Implementació de Subversion Name[ca@valencia]=Implementació de Subversion Name[da]=Subversion-understøttelse Name[de]=Subversion-Unterstützung Name[el]=Υποστήριξη Subversion Name[en_GB]=Subversion Support Name[es]=Implementación de Subversion Name[et]=Subversioni toetus Name[fr]=Prise en charge de Subversion Name[gl]=Soporte de Subversion Name[it]=Supporto subversion Name[ja]=Subversion サポート Name[nb]=Støtte for subversion Name[nds]=Subversion-Ünnerstütten Name[nl]=Ondersteuning van subversion Name[pa]=ਸਬਵਰਜਨ ਸਹਿਯੋਗ Name[pl]=Obsługa Subversion Name[pt]=Suporte para Subversion Name[pt_BR]=Suporte ao Subversion Name[ru]=Поддержка Subversion Name[sl]=Podpora za Subversion Name[sv]=Stöd för Subversion Name[tr]=Subversion Desteği Name[uk]=Підтримка Subversion Name[x-test]=xxSubversion Supportxx Name[zh_CN]=Subversion 支持 Name[zh_TW]=Subversion 支援 GenericName=Version Control Support GenericName[bg]=Поддръжка на управление на версиите GenericName[ca]=Implementació del control de versions GenericName[ca@valencia]=Implementació del control de versions GenericName[da]=Understøttelse af versionstyring GenericName[de]=Unterstützung für Versionsverwaltungssysteme GenericName[en_GB]=Version Control Support GenericName[es]=Implementación de Control de Versiones GenericName[et]=Versioonihalduse toetus GenericName[fr]=Prise en charge du contrôle de versions GenericName[gl]=Soporte de sistemas de versións GenericName[it]=Supporto controllo versione GenericName[ja]=バージョン管理のサポート GenericName[nb]=Støtte for versjonskontroll GenericName[nds]=Verschoonkuntrull-Ünnerstütten GenericName[nl]=Ondersteuning voor versiecontrole GenericName[pl]=Obsługa kontroli wersji GenericName[pt]=Suporte ao Controlo de Versões GenericName[pt_BR]=Suporte a controle de versão GenericName[ru]=Поддержка систем контроля версий GenericName[sl]=Podpora za nadzor različic GenericName[sv]=Stöd för versionskontroll GenericName[tr]=Sürüm Kontrol Desteği GenericName[uk]=Підтримка керування версіями GenericName[x-test]=xxVersion Control Supportxx GenericName[zh_CN]=版本控制支持 GenericName[zh_TW]=版本控制支援 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevsubversion X-KDE-PluginInfo-Name=kdevsubversion X-KDE-PluginInfo-Author=Dukju Ahn X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-Category=Version Control X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Interfaces=org.kdevelop.IBasicVersionControl X-KDevelop-IRequired=org.kdevelop.IOutputView X-KDevelop-Mode=GUI diff --git a/project/projectbuildsetmodel.cpp b/project/projectbuildsetmodel.cpp index 3ad7bb7c56..67deb303de 100644 --- a/project/projectbuildsetmodel.cpp +++ b/project/projectbuildsetmodel.cpp @@ -1,284 +1,284 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * Copyright 2009 Aleix Pol * * * * This program 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 program 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 General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "projectbuildsetmodel.h" #include #include #include #include #include #include #include #include #include #include "projectmodel.h" #include #include #include namespace KDevelop { BuildItem::BuildItem() { } BuildItem::BuildItem( const QStringList & itemPath ) : m_itemPath( itemPath ) { } BuildItem::BuildItem( KDevelop::ProjectBaseItem* item ) { initializeFromItem( item ); } BuildItem::BuildItem( const BuildItem& rhs ) { m_itemPath = rhs.itemPath(); } void BuildItem::initializeFromItem( KDevelop::ProjectBaseItem* item ) { Q_ASSERT(item); KDevelop::ProjectModel* model=KDevelop::ICore::self()->projectController()->projectModel(); m_itemPath = model->pathFromIndex(item->index()); } QString BuildItem::itemName() const { return m_itemPath.last(); } QString BuildItem::itemProject() const { return m_itemPath.first(); } KDevelop::ProjectBaseItem* BuildItem::findItem() const { KDevelop::ProjectModel* model=KDevelop::ICore::self()->projectController()->projectModel(); QModelIndex idx = model->pathToIndex(m_itemPath); KDevelop::ProjectBaseItem* item = dynamic_cast(model->itemFromIndex(idx)); return item; } bool operator==( const BuildItem& rhs, const BuildItem& lhs ) { return( rhs.itemPath() == lhs.itemPath() ); } BuildItem& BuildItem::operator=( const BuildItem& rhs ) { if( this == &rhs ) return *this; m_itemPath = rhs.itemPath(); return *this; } ProjectBuildSetModel::ProjectBuildSetModel( QObject* parent ) : QAbstractTableModel( parent ) { } QVariant ProjectBuildSetModel::data( const QModelIndex& idx, int role ) const { if( !idx.isValid() || idx.row() < 0 || idx.column() < 0 || idx.row() >= rowCount() || idx.column() >= columnCount()) { return QVariant(); } if(role == Qt::DisplayRole) { switch( idx.column() ) { case 0: return m_items.at( idx.row() ).itemName(); break; case 1: return KDevelop::joinWithEscaping( m_items.at( idx.row() ).itemPath(), '/', '\\'); break; } } else if(role == Qt::DecorationRole && idx.column()==0) { KDevelop::ProjectBaseItem* item = m_items.at( idx.row() ).findItem(); if( item ) { return KIcon( item->iconName() ); } } return QVariant(); } QVariant ProjectBuildSetModel::headerData( int section, Qt::Orientation orientation, int role ) const { if( section < 0 || section >= columnCount() || orientation != Qt::Horizontal || role != Qt::DisplayRole ) return QVariant(); switch( section ) { case 0: - return i18n("Name"); + return i18nc("@title:column buildset item name", "Name"); break; case 1: - return i18n("Path"); + return i18nc("@title:column buildset item path", "Path"); break; } return QVariant(); } int ProjectBuildSetModel::rowCount( const QModelIndex& parent ) const { if( parent.isValid() ) return 0; return m_items.count(); } int ProjectBuildSetModel::columnCount( const QModelIndex& parent ) const { if( parent.isValid() ) return 0; return 2; } void ProjectBuildSetModel::addProjectItem( KDevelop::ProjectBaseItem* item ) { if( m_items.contains( BuildItem(item) ) ) return; beginInsertRows( QModelIndex(), rowCount(), rowCount() ); m_items.append(BuildItem(item)); endInsertRows(); } bool ProjectBuildSetModel::removeRows( int row, int count, const QModelIndex& parent ) { if( parent.isValid() || row > rowCount() || row < 0 || (row+count) > rowCount() || count <= 0 ) return false; beginRemoveRows( QModelIndex(), row, row+count-1 ); for( int i = row; i < row+count; i++ ) { m_items.removeAt( row ); } endRemoveRows(); return true; } QList ProjectBuildSetModel::items() { return m_items ; } void ProjectBuildSetModel::projectClosed( KDevelop::IProject* project ) { for( int i = m_items.count() - 1; i >= 0; i-- ) { if( m_items.at(i).itemProject() == project->name()) { beginRemoveRows( QModelIndex(), i, i ); m_items.removeAt(i); endRemoveRows(); } } } void ProjectBuildSetModel::saveToProject( KDevelop::IProject* project ) const { QVariantList paths; foreach( const BuildItem &item, m_items) { if( item.itemProject() == project->name() ) paths.append(item.itemPath()); } KConfigGroup base = project->projectConfiguration()->group("Buildset"); base.writeEntry("BuildItems", KDevelop::qvariantToString( QVariant( paths ) )); base.sync(); } void ProjectBuildSetModel::loadFromProject( KDevelop::IProject* project ) { KConfigGroup base = project->projectConfiguration()->group("Buildset"); if (base.hasKey("BuildItems")) { QVariantList items = KDevelop::stringToQVariant(base.readEntry("BuildItems", QString())).toList(); foreach(const QVariant& path, items) { beginInsertRows( QModelIndex(), rowCount(), rowCount() ); m_items.append( BuildItem( path.toStringList() ) ); endInsertRows(); } } else { // Add project to buildset, but only if there is no configuration for this project yet. addProjectItem( project->projectItem() ); } } void ProjectBuildSetModel::moveRowsDown(int row, int count) { QList items = m_items.mid( row, count ); removeRows( row, count ); beginInsertRows( QModelIndex(), row+1, row+count ); for( int i = 0; i < count; i++ ) { m_items.insert( row+1+i, items.at( i ) ); } endInsertRows(); } void ProjectBuildSetModel::moveRowsToBottom(int row, int count) { QList items = m_items.mid( row, count ); removeRows( row, count ); beginInsertRows( QModelIndex(), rowCount(), rowCount()+count-1 ); m_items += items; endInsertRows(); } void ProjectBuildSetModel::moveRowsUp(int row, int count) { QList items = m_items.mid( row, count ); removeRows( row, count ); beginInsertRows( QModelIndex(), row-1, row-2+count ); for( int i = 0; i < count; i++ ) { m_items.insert( row-1+i, items.at( i ) ); } endInsertRows(); } void ProjectBuildSetModel::moveRowsToTop(int row, int count) { QList items = m_items.mid( row, count ); removeRows( row, count ); beginInsertRows( QModelIndex(), 0, count-1 ); for( int i = 0; i < count; i++ ) { m_items.insert( 0+i, items.at( i ) ); } endInsertRows(); } } diff --git a/shell/assistantpopup.cpp b/shell/assistantpopup.cpp index 4ce05b2069..fadc4eebb2 100644 --- a/shell/assistantpopup.cpp +++ b/shell/assistantpopup.cpp @@ -1,90 +1,92 @@ /* Copyright 2009 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 "assistantpopup.h" #include #include #include #include #include #include const int SPACING_FROM_PARENT_BOTTOM = 5; void AssistantPopup::updateActions() { QPalette palette = QApplication::palette(); palette.setBrush(QPalette::Background, palette.toolTipBase()); setPalette(palette); m_assistantActions = m_assistant->actions(); bool haveTitle = false; if (!m_assistant->title().isEmpty()) { haveTitle = true; QLabel* title = new QLabel("" + m_assistant->title() + ":"); title->setTextFormat(Qt::RichText); + // We need some margin at the left, and also an overall margin makes the popup look nicer + title->setMargin(2); addWidget(title); } ///@todo Add some intelligent layouting to make sure the widget doesn't become too wide foreach(KDevelop::IAssistantAction::Ptr action, m_assistantActions) { if(haveTitle || action != m_assistantActions.first()) addSeparator(); addWidget(widgetForAction(action)); } addSeparator(); addWidget(widgetForAction(KDevelop::IAssistantAction::Ptr())); resize(sizeHint()); move((parentWidget()->width() - width())/2, parentWidget()->height() - height() - SPACING_FROM_PARENT_BOTTOM); } AssistantPopup::AssistantPopup(QWidget* parent, KDevelop::IAssistant::Ptr assistant) : QToolBar(parent), m_assistant(assistant) { Q_ASSERT(assistant); setAutoFillBackground(true); updateActions(); } QWidget* AssistantPopup::widgetForAction(KDevelop::IAssistantAction::Ptr action) { KDevelop::RichTextToolButton* button = new KDevelop::RichTextToolButton; KAction* realAction; QString buttonText; int index = m_assistantActions.indexOf(action); if(index == -1) { realAction = new KAction(button); buttonText = "0 - " + i18n("Hide"); } else { realAction = action->toKAction(); buttonText = QString("%1 - ").arg(index+1) + action->description(); } realAction->setParent(button); connect(realAction, SIGNAL(triggered(bool)), SLOT(executeHideAction())); button->setDefaultAction(realAction); button->setText(QString("&%1").arg(index+1)); // Let the button care about the shortcut button->setHtml(buttonText); return button; } void AssistantPopup::executeHideAction() { m_assistant->doHide(); } KSharedPtr< KDevelop::IAssistant > AssistantPopup::assistant() const { return m_assistant; } #include "assistantpopup.moc" diff --git a/shell/kross/examples/bzrkdevelop/kdevbzr.desktop b/shell/kross/examples/bzrkdevelop/kdevbzr.desktop index 824b8d884f..93b95bd0a8 100644 --- a/shell/kross/examples/bzrkdevelop/kdevbzr.desktop +++ b/shell/kross/examples/bzrkdevelop/kdevbzr.desktop @@ -1,99 +1,99 @@ [Desktop Entry] Type=Service Exec=blubb Comment=This plugin integrates Bazaar to KDevelop Comment[bg]=Тази приставка вгражда Bazaar в KDevelop Comment[ca]=Aquest connector integra Bazaar en KDevelop Comment[ca@valencia]=Este connector integra Bazaar en KDevelop Comment[da]=Dette plugin integrerer Bazaar med KDevelop Comment[de]=Dieses Modul integriert Bazaar in KDevelop. Comment[el]=Αυτό το πρόσθετο ενσωματώνει το Bazaar στο KDevelop Comment[en_GB]=This plugin integrates Bazaar to KDevelop Comment[es]=Este complemento integra Bazaar en KDevelop Comment[et]=See plugin lõimib Bazaari KDeveloppi Comment[fr]=Ce module externe intègre Bazaar à KDevelop Comment[gl]=Este engadido integra Bazaar con KDevelop Comment[it]=Questa estensione integra Bazaar in KDevelop Comment[ja]=このプラグインは Bazaar を KDevelop に統合します Comment[lv]=Šis spraudnis integrē Bazaar iekš KDevelop Comment[nb]=Dette programtillegget integrerer Bazaar i KDevelop Comment[nds]=Dit Moduul bett Bazaar na KDevelop in Comment[nl]=Deze plugin integreert Bazaar in KDevelop Comment[pl]=Ta wtyczka udostępnia Bazaar w KDevelopie Comment[pt]=Este 'plugin' integra o Bazaar no KDevelop Comment[pt_BR]=Esta extensão integra o Bazaar ao KDevelop Comment[ro]=Acest modul integrează Bazaar în KDevelop -Comment[ru]=Этот модуль добавляет поддержку работы с Bazaar в KDevelop +Comment[ru]=Это расширение добавляет поддержку работы с Bazaar в KDevelop Comment[sl]=Vstavek v KDevelop integrira Bazaar Comment[sv]=Insticksprogrammet integrerar Bazaar i KDevelop Comment[tr]=Bu eklenti Bazaar uygulamasını KDevelop ile bütünleştirir Comment[uk]=За допомогою цього додатка можна інтегрувати Bazaar до KDevelop Comment[x-test]=xxThis plugin integrates Bazaar to KDevelopxx Comment[zh_CN]=此插件对 KDevelop 整合 Bazaar Comment[zh_TW]=此外掛程式將 Bazaar 整合進 KDevelop 內 Name=Bazaar Support Name[bg]=Поддръжка на Bazaar Name[ca]=Implementació de Bazaar Name[ca@valencia]=Implementació de Bazaar Name[da]=Bazaar-understøttelse Name[de]=Bazaar-Unterstützung Name[el]=Υποστήριξη Bazaar Name[en_GB]=Bazaar Support Name[es]=Implementación de Bazaar Name[et]=Bazaari toetus Name[fr]=Prise en charge de Bazaar Name[ga]=Tacaíocht Bazaar Name[it]=Supporto Bazaar Name[ja]=Bazaar サポート Name[nb]=GDB-støtte Name[nds]=Bazaar-Ünnerstütten Name[nl]=Ondersteuning van Bazaar Name[nn]=DPMS-støtte Name[pt]=Suporte para Bazaar Name[pt_BR]=Suporte a Bazaar Name[ru]=Поддержка Bazaar Name[sl]=Podpora za Bazaar Name[sv]=Bazaar-stöd Name[tr]=Bazaar Desteği Name[uk]=Підтримка Bazaar Name[x-test]=xxBazaar Supportxx Name[zh_CN]=Bazaar 支持 Name[zh_TW]=Bazaar 支援 GenericName=Version Control Support GenericName[bg]=Поддръжка на управление на версиите GenericName[ca]=Implementació del control de versions GenericName[ca@valencia]=Implementació del control de versions GenericName[da]=Understøttelse af versionstyring GenericName[de]=Unterstützung für Versionsverwaltungssysteme GenericName[en_GB]=Version Control Support GenericName[es]=Implementación de Control de Versiones GenericName[et]=Versioonihalduse toetus GenericName[fr]=Prise en charge du contrôle de versions GenericName[gl]=Soporte de sistemas de versións GenericName[it]=Supporto controllo versione GenericName[ja]=バージョン管理のサポート GenericName[nb]=Støtte for versjonskontroll GenericName[nds]=Verschoonkuntrull-Ünnerstütten GenericName[nl]=Ondersteuning voor versiecontrole GenericName[pl]=Obsługa kontroli wersji GenericName[pt]=Suporte ao Controlo de Versões GenericName[pt_BR]=Suporte a controle de versão GenericName[ru]=Поддержка систем контроля версий GenericName[sl]=Podpora za nadzor različic GenericName[sv]=Stöd för versionskontroll GenericName[tr]=Sürüm Kontrol Desteği GenericName[uk]=Підтримка керування версіями GenericName[x-test]=xxVersion Control Supportxx GenericName[zh_CN]=版本控制支持 GenericName[zh_TW]=版本控制支援 ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevbzr/kdevbzr.py X-KDE-PluginInfo-Name=kdevbzr X-KDE-PluginInfo-Author=Aleix Pol X-KDE-PluginInfo-Version=0.01 X-KDE-PluginInfo-License=GPL X-KDevelop-Version=11 X-KDevelop-Category=Global X-KDevelop-Properties=GlobalFileManagement X-KDevelop-PluginType=Kross X-KDevelop-Interfaces=org.kdevelop.IBasicVersionControl, IDistributedVersionControl diff --git a/shell/settings/kcm_kdev_uisettings.desktop b/shell/settings/kcm_kdev_uisettings.desktop index 84869b1a29..b6727c65bb 100644 --- a/shell/settings/kcm_kdev_uisettings.desktop +++ b/shell/settings/kcm_kdev_uisettings.desktop @@ -1,69 +1,70 @@ [Desktop Entry] Icon=kdevelop Type=Service ServiceTypes=KCModule X-KDE-ModuleType=Library X-KDE-Library=kcm_kdev_uisettings X-KDE-FactoryName=kcm_kdev_uisettings X-KDE-ParentApp=kdevplatform X-KDE-ParentComponents=kdevplatform X-KDE-CfgDlgHierarchy=USERINTERFACE X-KDE-Weight=2 Name=User Interface Name[bg]=Потребителски интерфейс Name[ca]=Interfície d'usuari Name[ca@valencia]=Interfície d'usuari Name[da]=Brugerflade Name[de]=Benutzeroberfläche Name[el]=Περιβάλλον χρήστη Name[en_GB]=User Interface Name[es]=Interfaz de usuario Name[et]=Kasutajaliides Name[fr]=Interface utilisateur Name[gl]=Interface de usuario Name[it]=Interfaccia utente Name[ja]=ユーザインターフェース Name[lv]=Lietotāja saskarne Name[nb]=Brukerflate Name[nds]=Böversiet Name[nl]=Gebruikersinterface Name[pa]=ਯੂਜ਼ਰ ਇੰਟਰਫੇਸ Name[pl]=Interfejs użytkownika Name[pt]=Interface do Utilizador Name[pt_BR]=Interface do usuário Name[ro]=Interfață utilizator Name[ru]=Пользовательский интерфейс Name[sl]=Uporabniški vmesnik Name[sv]=Användargränssnitt Name[tr]=Kullanıcı Arayüzü Name[uk]=Інтерфейс користувача Name[x-test]=xxUser Interfacexx Name[zh_CN]=用户界面 Name[zh_TW]=使用者介面 Comment=Configure User Interface Comment[bg]=Настройка на потребителски интерфейс Comment[ca]=Configura la interfície d'usuari Comment[ca@valencia]=Configura la interfície d'usuari Comment[da]=Indstil brugerflade Comment[de]=Benutzeroberfläche einrichten Comment[en_GB]=Configure User Interface Comment[es]=Configurar la interfaz de usuario Comment[et]=Kasutajaliidese seadistamine Comment[fr]=Configurer l'interface utilisateur Comment[it]=Configura l'intefaccia utente Comment[ja]=ユーザインターフェースを設定します Comment[nb]=Sett opp brukerflate Comment[nds]=Böversiet instellen Comment[nl]=Gebruikersinterface instellen Comment[pl]=Konfiguruj interfejs użytkownika Comment[pt]=Configurar a Interface do Utilizador Comment[pt_BR]=Configurar a interface do usuário +Comment[ru]=Настройка пользовательского интерфейса Comment[sl]=Nastavite uporabniški vmesnik Comment[sv]=Anpassa användargränssnitt Comment[uk]=Налаштування інтерфейсу користувача Comment[x-test]=xxConfigure User Interfacexx Comment[zh_CN]=配置用户界面 Comment[zh_TW]=設定使用者介面