Right now, in Kate's gitorious repository we, or rather mainly Christoph, is rewriting the heart of Kate: The text buffer. In this blog I'll explain how the text buffer works in detail. After reading, you'll be able to understand the code. And be warned: Probably you will be desperately amazed about its awesomeness! So this is a must read :^) The following content is based on the notes for design ideas for the new text buffer.
Storage. Kate Part internally stores the content of a text document as a QVector of text lines. Each text line holds the text in a QString object as well as additional data like color attributes for highlighting. Inserting and removing text lines implies inserting or removing items in the QVector of the text lines. This is can get slow when there are thousands of text lines in a document, because QVector has to move the memory every time. Thus, to keep text operations fast, Kate splits all text lines into several text blocks: Each text block contains a certain amount of lines (e.g. 256). The expense of memory movement is then always limited. When a text block grows too much, it is automatically split. When the amount of lines in a text block shrinks, it is merged with the previous text block. In other words, Kate's text buffer automatically balances the text blocks. The basic idea of Kate's text buffer, the text blocks and text lines looks like this:
Text Cursors and Ranges. Text manipulation always takes place at certain positions in a text. Such a position is called a text cursor. Each text cursor is defined by a line and a column. Further, to specify e.g. text selections, we need text ranges. A text range has a start cursor and an end cursor. For instance, if you have two views of the same document, you want the cursor in the 2nd view to keep its correct position in the text if you insert text in the 1st view. Thus, text cursors have to be kind of intelligent. Whenever text changes, each text cursor needs to be moved. As a text range just consists of two text cursors, we will focus only on text cursors for now. The question is how to implement this efficiently? If we store all text cursors in a list in the text buffer, we have to iterate over all text cursors on every editing operation. This obviously does not scale for thousands of text cursors (KDevelop for instance uses thousands of text cursors for the highlighting). The solution is the same as with the text content itself: Let's just put a list of all text cursors in a text block to the text block itself. During text editing, we only have to adapt all text cursors of a single text block instead of all text cursors in a document. This looks as follows:
Editing Operations. When editing text, you usually have only four different types of text manipulation:
Transactions. A transaction consists of several of the four editing operations. For instance, if you select text and then move it to another location in the document with drag & drop you want this to be grouped together to a single text operation on undo/redo. (The text operations in this case are unwrap lines and remove text, then insert text and wrap line). To be able to specify which parts of text operations belong together, the text buffer provides two functions: startEditing() starts a transaction and finishEditing() closes it. Those functions use reference counting, so you can call startEditing() multiple times and only the last finishEditing() completes a transaction. Again, signals are emitted e.g. in finishEditing() such that other layers (undo/redo system) are notified about this.
Revisions. As an easy way to check whether a document changed you can get the current revision of the text buffer. The revision is simply an int64 starting with 0 after loading a document. The revision is incremented in every of the 4 editing primitives. This way you don't have to listen to multiple signals like textInserted() and textRemoved(). This could be useful for e.g. KDevelop's background parser: Parse a file. When done, check whether the revision is the same. If yes, parsing was successful. If not, parse again. This is how QtCreator does it. Easy and straight forward.
Unit Tests. The text buffer, text block, text line, text cursors are all designed such that unit tests can be written. Hence, each of the above implementation details will be covered by unit tests.
Further ideas. As mentioned previously, the design of the text buffer leaves room for further features. For now, let's face two ideas:
Please Contribute.
Det er længe siden jeg har skrevet her, af forskellige årsager, så her følger lige en opdatering.
Alweb.dk har været delvis nede fordi en tabel i databasen var brudt sammen, det var heldigvil let nok at reparere. (Man kunne ikke logge ind, eller se andre sider end forsiden. Det har ikke holdt mig tilbage, jeg vidste det simpelthen ikke.)
Vi har fået ny leder på mit arbejde, og det er trættende — ikke fordi der er noget i vejen med hende, men det giver jo uro i arbejdsformerne. Forhåbentlig kommer der gode ting ud af det, meget peger den vej.
Og så har jeg forresten fået lidt flere timer, jeg arbejder 35 timer om ugen for tiden. Det giver en lille smule mere i løn, og en del mindre tid.
Jeg har skiftet styresystem, jeg blev træt af KUbuntu fordi de bestemte at det ikke var nødvendigt at understøtte KDE 3 i den nye version, og det er nødvendigt for mig. Jeg kunne have beholdt den gamle version, men så løber man ind i problemer fordi KUbuntu ikke laver opdateringer af programmerne udover sikkerheds-. Altså, farvel og tak til KUbuntu som jeg ellers har været glad for at bruge i ret lang tid.
Jeg forsøgte med Debian, men jeg blev sur på det fordi valg af ‘desktop’ betød installation af en masse programmer jeg ikke vil bruge. Hvis jeg skal vende tilbage til Debian engang, vil jeg starte med den mindst mulige installation.
Så forsøgte jeg med OpenSUSE, men det fungerede simpelthen ikke. For at få de programmer jeg skal bruge skulle jeg installere fra forskellige kilder, og deres softwaremanager havde (jeg håber for dem at det er løst) en fejl så den ikke valgte den korrekte version af programmerne til installation. Og selv da jeg endelig fik installeret det jeg ville, var der hele tiden sammenbrud og problemer. Understøttelsen af KDE 3 var der, men problematisk, jeg kunne f.eks. ikke installere hjælp for kde3 programmer.
Nu har jeg installeret Arch Linux, og det er godt synes jeg. Der installeres en minimal version, og man installerer så efterfølgende hvad man vil. Softwarehåndteringen er glimrende, let at forstå og bruge. I Arch Linux skal man konfigurere al ting selv, det er lidt omstændeligt, men til gengæld har de en virkelig god dokumentation på deres wiki. Der er nogle ting der ikke fungerer endnu, suspend er en og knetworkmanager en anden (det starter, men kan ikke forbinde, pga konfigurationsfejl tror jeg). En af de store fordele ved Arch Linux er at de har løbende udgivelse af opdateret software. Dermed kan man få nye versioner når de kommer, men man kan selvfølgelig beholde kritiske dele af systemet som man ønsker.
Jeg har bygget en del af KDEs udviklingsversion forleden, så jeg kan komme igang med at hjælpe lidt med Kate igen. Jeg har allerede sendt det første simple bidrag :-)
Og så er jeg igang med at hjælpe lidt med de danske oversættelser af KDE, mest som korrektur-læser, men det er jo også en en vigtig opgave. Det er spændende og giver nogle sjove oplevelser ind i mellem når oversætteren ikke aner hvad teksten handler om, eller har for travlt til at opdage nogle af de sjove ord, forleden stødte jeg på “tidsintervaldørstopper” ;)
Idag sidder jeg og tænker på økonomi — hvad skal der til at male/vedligeholde Kate, har jeg råd til at købe et kamera? Jeg kigger i den blå avis efter et godt digitalt spejlreflekskamera, noget i retning af et Canon EOS 30D, som kan fås til en ret god pris. Det kunne være sjovt, men jeg skal altså lige tænke mig om…