• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdevelop API Reference
  • KDE Home
  • Contact Us
 

kdevplatform/language/duchain

  • sources
  • kfour-appscomplete
  • kdevelop
  • kdevplatform
  • language
  • duchain
persistentsymboltable.cpp
Go to the documentation of this file.
1 /* This file is part of KDevelop
2  Copyright 2008 David Nolden <[email protected]>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License version 2 as published by the Free Software Foundation.
7 
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  Library General Public License for more details.
12 
13  You should have received a copy of the GNU Library General Public License
14  along with this library; see the file COPYING.LIB. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17  */
18 
19 #include "persistentsymboltable.h"
20 
21 #include <QHash>
22 
23 #include "declaration.h"
24 #include "declarationid.h"
25 #include "appendedlist.h"
26 #include "serialization/itemrepository.h"
27 #include "identifier.h"
28 #include "ducontext.h"
29 #include "topducontext.h"
30 #include "duchain.h"
31 #include "duchainlock.h"
32 #include <util/embeddedfreetree.h>
33 
34 //For now, just _always_ use the cache
35 const uint MinimumCountForCache = 1;
36 
37 namespace {
38 QDebug fromTextStream(const QTextStream& out)
39 {
40  if (out.device())
41  return {
42  out.device()
43  }; return {
44  out.string()
45  };
46 }
47 }
48 
49 namespace KDevelop {
50 
51 using TextStreamFunction = QTextStream& (*)(QTextStream&);
52 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
53 constexpr TextStreamFunction endl = Qt::endl;
54 #else
55 constexpr TextStreamFunction endl = ::endl;
56 #endif
57 
58 Utils::BasicSetRepository* RecursiveImportCacheRepository::repository()
59 {
60  static Utils::BasicSetRepository recursiveImportCacheRepositoryObject(QStringLiteral(
61  "Recursive Imports Cache"), nullptr, false);
62  return &recursiveImportCacheRepositoryObject;
63 }
64 
65 DEFINE_LIST_MEMBER_HASH(PersistentSymbolTableItem, declarations, IndexedDeclaration)
66 
67 class PersistentSymbolTableItem
68 {
69 public:
70  PersistentSymbolTableItem() : centralFreeItem(-1)
71  {
72  initializeAppendedLists();
73  }
74  PersistentSymbolTableItem(const PersistentSymbolTableItem& rhs, bool dynamic = true) : id(rhs.id)
75  , centralFreeItem(rhs.centralFreeItem)
76  {
77  initializeAppendedLists(dynamic);
78  copyListsFrom(rhs);
79  }
80 
81  ~PersistentSymbolTableItem()
82  {
83  freeAppendedLists();
84  }
85 
86  PersistentSymbolTableItem& operator=(const PersistentSymbolTableItem& rhs) = delete;
87 
88  inline unsigned int hash() const
89  {
90  //We only compare the declaration. This allows us implementing a map, although the item-repository
91  //originally represents a set.
92  return id.index();
93  }
94 
95  unsigned int itemSize() const
96  {
97  return dynamicSize();
98  }
99 
100  uint classSize() const
101  {
102  return sizeof(PersistentSymbolTableItem);
103  }
104 
105  IndexedQualifiedIdentifier id;
106  int centralFreeItem;
107 
108  START_APPENDED_LISTS(PersistentSymbolTableItem);
109  APPENDED_LIST_FIRST(PersistentSymbolTableItem, IndexedDeclaration, declarations);
110  END_APPENDED_LISTS(PersistentSymbolTableItem, declarations);
111 };
112 
113 class PersistentSymbolTableRequestItem
114 {
115 public:
116 
117  PersistentSymbolTableRequestItem(const PersistentSymbolTableItem& item) : m_item(item)
118  {
119  }
120  enum {
121  AverageSize = 30 //This should be the approximate average size of an Item
122  };
123 
124  unsigned int hash() const
125  {
126  return m_item.hash();
127  }
128 
129  uint itemSize() const
130  {
131  return m_item.itemSize();
132  }
133 
134  void createItem(PersistentSymbolTableItem* item) const
135  {
136  new (item) PersistentSymbolTableItem(m_item, false);
137  }
138 
139  static void destroy(PersistentSymbolTableItem* item, KDevelop::AbstractItemRepository&)
140  {
141  item->~PersistentSymbolTableItem();
142  }
143 
144  static bool persistent(const PersistentSymbolTableItem*)
145  {
146  return true; //Nothing to do
147  }
148 
149  bool equals(const PersistentSymbolTableItem* item) const
150  {
151  return m_item.id == item->id;
152  }
153 
154  const PersistentSymbolTableItem& m_item;
155 };
156 
157 template <class ValueType>
158 struct CacheEntry
159 {
160  using Data = KDevVarLengthArray<ValueType>;
161  using DataHash = QHash<TopDUContext::IndexedRecursiveImports, Data>;
162 
163  DataHash m_hash;
164 };
165 
166 class PersistentSymbolTablePrivate
167 {
168 public:
169 
170  PersistentSymbolTablePrivate() : m_declarations(QStringLiteral("Persistent Declaration Table"))
171  {
172  }
173  //Maps declaration-ids to declarations
174  // mutable as things like findIndex are not const
175  mutable ItemRepository<PersistentSymbolTableItem, PersistentSymbolTableRequestItem, true, false> m_declarations;
176 
177  mutable QHash<IndexedQualifiedIdentifier, CacheEntry<IndexedDeclaration>> m_declarationsCache;
178 
179  //We cache the imports so the currently used nodes are very close in memory, which leads to much better CPU cache utilization
180  mutable QHash<TopDUContext::IndexedRecursiveImports, PersistentSymbolTable::CachedIndexedRecursiveImports> m_importsCache;
181 };
182 
183 void PersistentSymbolTable::clearCache()
184 {
185  Q_D(PersistentSymbolTable);
186 
187  ENSURE_CHAIN_WRITE_LOCKED
188  {
189  QMutexLocker lock(d->m_declarations.mutex());
190  d->m_importsCache.clear();
191  d->m_declarationsCache.clear();
192  }
193 }
194 
195 PersistentSymbolTable::PersistentSymbolTable()
196  : d_ptr(new PersistentSymbolTablePrivate())
197 {
198 }
199 
200 PersistentSymbolTable::~PersistentSymbolTable()
201 {
202  //Workaround for a strange destruction-order related crash duing shutdown
203  //We just let the data leak. This doesn't hurt, as there is no meaningful destructors.
204  // TODO: analyze and fix it
205 // delete d;
206 }
207 
208 void PersistentSymbolTable::addDeclaration(const IndexedQualifiedIdentifier& id, const IndexedDeclaration& declaration)
209 {
210  Q_D(PersistentSymbolTable);
211 
212  QMutexLocker lock(d->m_declarations.mutex());
213  ENSURE_CHAIN_WRITE_LOCKED
214 
215  d->m_declarationsCache.remove(id);
216 
217  PersistentSymbolTableItem item;
218  item.id = id;
219  PersistentSymbolTableRequestItem request(item);
220 
221  uint index = d->m_declarations.findIndex(item);
222 
223  if (index) {
224  //Check whether the item is already in the mapped list, else copy the list into the new created item
225  const PersistentSymbolTableItem* oldItem = d->m_declarations.itemFromIndex(index);
226 
227  EmbeddedTreeAlgorithms<IndexedDeclaration, IndexedDeclarationHandler> alg(
228  oldItem->declarations(), oldItem->declarationsSize(), oldItem->centralFreeItem);
229 
230  if (alg.indexOf(declaration) != -1)
231  return;
232 
233  DynamicItem<PersistentSymbolTableItem, true> editableItem = d->m_declarations.dynamicItemFromIndex(index);
234 
235  EmbeddedTreeAddItem<IndexedDeclaration, IndexedDeclarationHandler> add(
236  const_cast<IndexedDeclaration*>(editableItem->declarations()),
237  editableItem->declarationsSize(), editableItem->centralFreeItem, declaration);
238 
239  uint newSize = add.newItemCount();
240  if (newSize != editableItem->declarationsSize()) {
241  //We need to resize. Update and fill the new item, and delete the old item.
242  item.declarationsList().resize(newSize);
243  add.transferData(item.declarationsList().data(), newSize, &item.centralFreeItem);
244 
245  d->m_declarations.deleteItem(index);
246  Q_ASSERT(!d->m_declarations.findIndex(request));
247  } else {
248  //We're fine, the item could be added to the existing list
249  return;
250  }
251  } else {
252  item.declarationsList().append(declaration);
253  }
254 
255  //This inserts the changed item
256  d->m_declarations.index(request);
257 }
258 
259 void PersistentSymbolTable::removeDeclaration(const IndexedQualifiedIdentifier& id,
260  const IndexedDeclaration& declaration)
261 {
262  Q_D(PersistentSymbolTable);
263 
264  QMutexLocker lock(d->m_declarations.mutex());
265  ENSURE_CHAIN_WRITE_LOCKED
266 
267  d->m_declarationsCache.remove(id);
268  Q_ASSERT(!d->m_declarationsCache.contains(id));
269 
270  PersistentSymbolTableItem item;
271  item.id = id;
272  PersistentSymbolTableRequestItem request(item);
273 
274  uint index = d->m_declarations.findIndex(item);
275 
276  if (index) {
277  //Check whether the item is already in the mapped list, else copy the list into the new created item
278  const PersistentSymbolTableItem* oldItem = d->m_declarations.itemFromIndex(index);
279 
280  EmbeddedTreeAlgorithms<IndexedDeclaration, IndexedDeclarationHandler> alg(
281  oldItem->declarations(), oldItem->declarationsSize(), oldItem->centralFreeItem);
282 
283  if (alg.indexOf(declaration) == -1)
284  return;
285 
286  DynamicItem<PersistentSymbolTableItem, true> editableItem = d->m_declarations.dynamicItemFromIndex(index);
287 
288  EmbeddedTreeRemoveItem<IndexedDeclaration, IndexedDeclarationHandler> remove(
289  const_cast<IndexedDeclaration*>(editableItem->declarations()),
290  editableItem->declarationsSize(), editableItem->centralFreeItem, declaration);
291 
292  uint newSize = remove.newItemCount();
293  if (newSize != editableItem->declarationsSize()) {
294  //We need to resize. Update and fill the new item, and delete the old item.
295  item.declarationsList().resize(newSize);
296  remove.transferData(item.declarationsList().data(), newSize, &item.centralFreeItem);
297 
298  d->m_declarations.deleteItem(index);
299  Q_ASSERT(!d->m_declarations.findIndex(request));
300  } else {
301  //We're fine, the item could be added to the existing list
302  return;
303  }
304  }
305 
306  //This inserts the changed item
307  if (item.declarationsSize())
308  d->m_declarations.index(request);
309 }
310 
311 struct DeclarationCacheVisitor
312 {
313  explicit DeclarationCacheVisitor(KDevVarLengthArray<IndexedDeclaration>& _cache) : cache(_cache)
314  {
315  }
316 
317  bool operator()(const IndexedDeclaration& decl) const
318  {
319  cache.append(decl);
320  return true;
321  }
322 
323  KDevVarLengthArray<IndexedDeclaration>& cache;
324 };
325 
326 PersistentSymbolTable::FilteredDeclarationIterator PersistentSymbolTable::filteredDeclarations(
327  const IndexedQualifiedIdentifier& id, const TopDUContext::IndexedRecursiveImports& visibility) const
328 {
329  Q_D(const PersistentSymbolTable);
330 
331  QMutexLocker lock(d->m_declarations.mutex());
332  ENSURE_CHAIN_READ_LOCKED
333 
334  Declarations decls = declarations(id).iterator();
335 
336  CachedIndexedRecursiveImports cachedImports;
337 
338  QHash<TopDUContext::IndexedRecursiveImports,
339  CachedIndexedRecursiveImports>::const_iterator it = d->m_importsCache.constFind(visibility);
340  if (it != d->m_importsCache.constEnd()) {
341  cachedImports = *it;
342  } else {
343  cachedImports = CachedIndexedRecursiveImports(visibility.set().stdSet());
344  d->m_importsCache.insert(visibility, cachedImports);
345  }
346 
347  if (decls.dataSize() > MinimumCountForCache) {
348  //Do visibility caching
349  CacheEntry<IndexedDeclaration>& cached(d->m_declarationsCache[id]);
350  CacheEntry<IndexedDeclaration>::DataHash::const_iterator cacheIt = cached.m_hash.constFind(visibility);
351  if (cacheIt != cached.m_hash.constEnd())
352  return FilteredDeclarationIterator(Declarations::Iterator(cacheIt->constData(),
353  cacheIt->size(), -1), cachedImports);
354 
355  CacheEntry<IndexedDeclaration>::DataHash::iterator insertIt = cached.m_hash.insert(visibility,
356  KDevVarLengthArray<IndexedDeclaration>());
357 
358  KDevVarLengthArray<IndexedDeclaration>& cache(*insertIt);
359 
360  {
361  using FilteredDeclarationCacheVisitor =
362  ConvenientEmbeddedSetTreeFilterVisitor<IndexedDeclaration, IndexedDeclarationHandler,
363  IndexedTopDUContext, CachedIndexedRecursiveImports, DeclarationTopContextExtractor,
364  DeclarationCacheVisitor>;
365 
366  //The visitor visits all the declarations from within its constructor
367  DeclarationCacheVisitor v(cache);
368  FilteredDeclarationCacheVisitor visitor(v, decls.iterator(), cachedImports);
369  }
370 
371  return FilteredDeclarationIterator(Declarations::Iterator(cache.constData(),
372  cache.size(), -1), cachedImports, true);
373  } else {
374  return FilteredDeclarationIterator(decls.iterator(), cachedImports);
375  }
376 }
377 
378 PersistentSymbolTable::Declarations PersistentSymbolTable::declarations(const IndexedQualifiedIdentifier& id) const
379 {
380  Q_D(const PersistentSymbolTable);
381 
382  QMutexLocker lock(d->m_declarations.mutex());
383  ENSURE_CHAIN_READ_LOCKED
384 
385  PersistentSymbolTableItem item;
386  item.id = id;
387 
388  uint index = d->m_declarations.findIndex(item);
389 
390  if (index) {
391  const PersistentSymbolTableItem* repositoryItem = d->m_declarations.itemFromIndex(index);
392  return PersistentSymbolTable::Declarations(repositoryItem->declarations(),
393  repositoryItem->declarationsSize(), repositoryItem->centralFreeItem);
394  } else {
395  return PersistentSymbolTable::Declarations();
396  }
397 }
398 
399 void PersistentSymbolTable::declarations(const IndexedQualifiedIdentifier& id, uint& countTarget,
400  const IndexedDeclaration*& declarationsTarget) const
401 {
402  Q_D(const PersistentSymbolTable);
403 
404  QMutexLocker lock(d->m_declarations.mutex());
405  ENSURE_CHAIN_READ_LOCKED
406 
407  PersistentSymbolTableItem item;
408  item.id = id;
409 
410  uint index = d->m_declarations.findIndex(item);
411 
412  if (index) {
413  const PersistentSymbolTableItem* repositoryItem = d->m_declarations.itemFromIndex(index);
414  countTarget = repositoryItem->declarationsSize();
415  declarationsTarget = repositoryItem->declarations();
416  } else {
417  countTarget = 0;
418  declarationsTarget = nullptr;
419  }
420 }
421 
422 struct DebugVisitor
423 {
424  explicit DebugVisitor(const QTextStream& _out)
425  : out(_out)
426  {
427  }
428 
429  bool operator()(const PersistentSymbolTableItem* item)
430  {
431  QDebug qout = fromTextStream(out);
432  QualifiedIdentifier id(item->id.identifier());
433  if (identifiers.contains(id)) {
434  qout << "identifier" << id.toString() << "appears for" << identifiers[id] << "th time";
435  }
436 
437  ++identifiers[id];
438 
439  for (uint a = 0; a < item->declarationsSize(); ++a) {
440  IndexedDeclaration decl(item->declarations()[a]);
441  if (!decl.isDummy()) {
442  if (declarations.contains(decl)) {
443  qout << "declaration found for multiple identifiers. Previous identifier:" <<
444  declarations[decl].toString() << "current identifier:" << id.toString() << endl;
445  } else {
446  declarations.insert(decl, id);
447  }
448  }
449  if (decl.data() && decl.data()->qualifiedIdentifier() != item->id.identifier()) {
450  qout << decl.data()->url().str() << "declaration" << decl.data()->qualifiedIdentifier() <<
451  "is registered as" << item->id.identifier() << endl;
452  }
453 
454  const QString url = IndexedTopDUContext(decl.topContextIndex()).url().str();
455  if (!decl.data() && !decl.isDummy()) {
456  qout << "Item in symbol-table is invalid:" << id.toString() << "- localIndex:" << decl.localIndex() <<
457  "- url:" << url << endl;
458  } else {
459  qout << "Item in symbol-table:" << id.toString() << "- localIndex:" << decl.localIndex() << "- url:" <<
460  url;
461  if (auto d = decl.data()) {
462  qout << "- range:" << d->range();
463  } else {
464  qout << "- null declaration";
465  }
466  qout << endl;
467  }
468  }
469 
470  return true;
471  }
472 
473  const QTextStream& out;
474  QHash<QualifiedIdentifier, uint> identifiers;
475  QHash<IndexedDeclaration, QualifiedIdentifier> declarations;
476 };
477 
478 void PersistentSymbolTable::dump(const QTextStream& out)
479 {
480  Q_D(PersistentSymbolTable);
481 
482  {
483  QMutexLocker lock(d->m_declarations.mutex());
484 
485  QDebug qout = fromTextStream(out);
486  DebugVisitor v(out);
487  d->m_declarations.visitAllItems(v);
488 
489  qout << "Statistics:" << endl;
490  qout << d->m_declarations.statistics() << endl;
491  }
492 }
493 
494 PersistentSymbolTable& PersistentSymbolTable::self()
495 {
496  static PersistentSymbolTable ret;
497  return ret;
498 }
499 }
duchainlock.h
KDevelop::IndexedTopDUContext
Allows simple indirect access to top-contexts with on-demand loading.
Definition: indexedtopducontext.h:35
QTextStream::string
QString * string() const
KDevelop::PersistentSymbolTable::~PersistentSymbolTable
~PersistentSymbolTable()
Destructor.
Definition: persistentsymboltable.cpp:200
KDevelop::PersistentSymbolTable::self
static PersistentSymbolTable & self()
Definition: persistentsymboltable.cpp:494
KDevelop::TextStreamFunction
QTextStream &(*)(QTextStream &) TextStreamFunction
Definition: definitions.cpp:31
DEFINE_LIST_MEMBER_HASH
#define DEFINE_LIST_MEMBER_HASH(container, member, type)
Definition: appendedlist.h:218
APPENDED_LIST_FIRST
#define APPENDED_LIST_FIRST(container, type, name)
Definition: appendedlist.h:321
MinimumCountForCache
const uint MinimumCountForCache
Definition: persistentsymboltable.cpp:35
KDevelop::endl
constexpr TextStreamFunction endl
Definition: definitions.cpp:33
KDevelop::PersistentSymbolTable::FilteredDeclarationIterator
ConvenientEmbeddedSetTreeFilterIterator< IndexedDeclaration, IndexedDeclarationHandler, IndexedTopDUContext, CachedIndexedRecursiveImports, DeclarationTopContextExtractor > FilteredDeclarationIterator
Definition: persistentsymboltable.h:142
KDevelop::PersistentSymbolTable::clearCache
void clearCache()
Definition: persistentsymboltable.cpp:183
QTextStream::device
QIODevice * device() const
QDebug
appendedlist.h
KDevelop::TopDUContext::IndexedRecursiveImports
Utils::StorableSet< IndexedTopDUContext, IndexedTopDUContextIndexConversion, RecursiveImportRepository, true > IndexedRecursiveImports
Definition: topducontext.h:290
topducontext.h
KDevelop::IndexedQualifiedIdentifier
A helper-class to store an identifier by index in a type-safe way.
Definition: identifier.h:95
KDevelop::PersistentSymbolTable::declarations
void declarations(const IndexedQualifiedIdentifier &id, uint &count, const IndexedDeclaration *&declarations) const
Retrieves all the declarations for a given IndexedQualifiedIdentifier in an efficient way.
Definition: persistentsymboltable.cpp:399
ENSURE_CHAIN_READ_LOCKED
#define ENSURE_CHAIN_READ_LOCKED
Macros for ensuring the DUChain is locked properly.
Definition: duchainlock.h:37
KDevelop::IndexedDeclaration
Represents a declaration only by its global indices.
Definition: indexeddeclaration.h:33
declaration.h
KDevelop::IndexedDeclarationHandler
Definition: persistentsymboltable.h:39
ENSURE_CHAIN_WRITE_LOCKED
#define ENSURE_CHAIN_WRITE_LOCKED
Definition: duchainlock.h:40
QString
identifier.h
KDevelop::DeclarationTopContextExtractor
Definition: persistentsymboltable.h:81
QTextStream
KDevVarLengthArray
KDevelop::PersistentSymbolTable::removeDeclaration
void removeDeclaration(const IndexedQualifiedIdentifier &id, const IndexedDeclaration &declaration)
Adds declaration declaration with id id to the symbol table.
Definition: persistentsymboltable.cpp:259
declarationid.h
KDevelop::PersistentSymbolTable::filteredDeclarations
FilteredDeclarationIterator filteredDeclarations(const IndexedQualifiedIdentifier &id, const TopDUContext::IndexedRecursiveImports &visibility) const
Retrieves an iterator to all declarations of the given id, filtered by the visibility given through v...
Definition: persistentsymboltable.cpp:326
END_APPENDED_LISTS
#define END_APPENDED_LISTS(container, predecessor)
Definition: appendedlist.h:358
persistentsymboltable.h
KDevelop::PersistentSymbolTable
Global symbol-table that is stored to disk, and allows retrieving declarations that currently are not...
Definition: persistentsymboltable.h:105
duchain.h
KDevelop
Definition: abstractfunctiondeclaration.cpp:27
KDevelop::RecursiveImportCacheRepository::repository
static Utils::BasicSetRepository * repository()
Definition: persistentsymboltable.cpp:58
QMutexLocker
KDevelop::PersistentSymbolTable::dump
void dump(const QTextStream &out)
Definition: persistentsymboltable.cpp:478
KDevelop::PersistentSymbolTable::addDeclaration
void addDeclaration(const IndexedQualifiedIdentifier &id, const IndexedDeclaration &declaration)
Adds declaration declaration with id id to the symbol table.
Definition: persistentsymboltable.cpp:208
KDevelop::PersistentSymbolTable::PersistentSymbolTable
PersistentSymbolTable()
Constructor.
Definition: persistentsymboltable.cpp:195
KDevelop::PersistentSymbolTable::Declarations
ConstantConvenientEmbeddedSet< IndexedDeclaration, IndexedDeclarationHandler > Declarations
Definition: persistentsymboltable.h:128
QHash< TopDUContext::IndexedRecursiveImports, Data >
ducontext.h
START_APPENDED_LISTS
#define START_APPENDED_LISTS(container)
use this if the class does not have a base class that also uses appended lists
Definition: appendedlist.h:250
KDevelop::PersistentSymbolTable::CachedIndexedRecursiveImports
Utils::StorableSet< IndexedTopDUContext, IndexedTopDUContextIndexConversion, RecursiveImportCacheRepository, true > CachedIndexedRecursiveImports
Definition: persistentsymboltable.h:138
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Jan 25 2021 23:36:50 by doxygen 1.8.16 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kdevplatform/language/duchain

Skip menu "kdevplatform/language/duchain"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdevelop API Reference

Skip menu "kdevelop API Reference"
  • kdevplatform
  •   debugger
  •   documentation
  •   interfaces
  •   language
  •     assistant
  •     backgroundparser
  •     checks
  •     classmodel
  •     codecompletion
  •     codegen
  •     duchain
  •     editor
  •     highlighting
  •     interfaces
  •     util
  •   outputview
  •   project
  •   serialization
  •   shell
  •   sublime
  •   tests
  •   util
  •   vcs

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal