[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kwrite-devel
Subject:    XML indentation mode
From:       Laurence Withers <lwithers () users ! sourceforge ! net>
Date:       2004-09-30 9:29:47
Message-ID: 200409301029.53014.lwithers () users ! sf ! net
[Download RAW message or body]

[Attachment #2 (multipart/signed)]

[Attachment #4 (multipart/mixed)]


Hi,

Working with Anders, I have developed an XML indentation mode. This mode 
will indent once per open element, so you end up with documents like:

<a>
  <b>
    <c>Some text and stuff <d>blah</d>
    </c>
  </b>
</a>

It ignores empty elements, processing instructions and CDATA sections 
(these shouldn't affect the indentation). However, it also won't 
recognise multi-line open or close tags (both of which are legal 
according to the XML spec). This is a limitation caused by the use of 
regular expressions.

I will maintain this mode as necessary, but it is useful as-is and I 
would appreciate the feedback from users before making any further 
changes.

Bye for now,
-- 
Laurence Withers, lwithers@users.sf.net, jabber:l.withers@jabber.org
http://www.personal.rdg.ac.uk/~sis04lw/

["katepart-xmlindent.patch" (text/x-diff)]

diff -ru part/kateautoindent.cpp katepart-xmlindent/kateautoindent.cpp
--- part/kateautoindent.cpp	2004-06-22 18:36:33.000000000 +0100
+++ katepart-xmlindent/kateautoindent.cpp	2004-09-30 10:26:51.036860750 +0100
@@ -33,6 +33,8 @@
     return new KateCSmartIndent (doc);
   else if (mode == KateDocumentConfig::imPythonStyle)
     return new KatePythonIndent (doc);
+  else if (mode == KateDocumentConfig::imXmlStyle)
+    return new KateXmlIndent (doc);
 
   return new KateAutoIndent (doc);
 }
@@ -44,6 +46,7 @@
   l << modeDescription(KateDocumentConfig::imNormal);
   l << modeDescription(KateDocumentConfig::imCStyle);
   l << modeDescription(KateDocumentConfig::imPythonStyle);
+  l << modeDescription(KateDocumentConfig::imXmlStyle);
 
   return l;
 }
@@ -54,6 +57,8 @@
     return QString ("cstyle");
   else if (mode == KateDocumentConfig::imPythonStyle)
     return QString ("python");
+  else if (mode == KateDocumentConfig::imXmlStyle)
+    return QString ("xml");
 
   return QString ("normal");
 }
@@ -64,6 +69,8 @@
     return i18n ("C Style");
   else if (mode == KateDocumentConfig::imPythonStyle)
     return i18n ("Python Style");
+  else if (mode == KateDocumentConfig::imXmlStyle)
+    return i18n ("XML Style");
 
   return i18n ("Normal");
 }
@@ -74,6 +81,8 @@
     return KateDocumentConfig::imCStyle;
   else if (modeName(KateDocumentConfig::imPythonStyle) == name)
     return KateDocumentConfig::imPythonStyle;
+  else if (modeName(KateDocumentConfig::imXmlStyle) == name)
+    return KateDocumentConfig::imPythonStyle;
 
   return KateDocumentConfig::imNormal;
 }
@@ -1006,4 +1015,167 @@
 
 // END
 
+// BEGIN KateXmlIndent
+
+QRegExp KateXmlIndent::openTag = QRegExp( "(<[^\?!/][^>]*[^/]>)|(<[^\?!/>]>)" );
+QRegExp KateXmlIndent::closeTag = QRegExp( "</[^>]*>" );
+QRegExp KateXmlIndent::startsWithCloseTag = QRegExp( "^[ \t]*</" );
+QRegExp KateXmlIndent::openOrCloseTag = QRegExp( "(<[^\?!][^>]*[^/]>)|(<[^\?!/>]>)" );
+
+KateXmlIndent::KateXmlIndent (KateDocument *doc)
+  : KateAutoIndent (doc)
+{
+}
+
+KateXmlIndent::~KateXmlIndent ()
+{
+}
+
+void KateXmlIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
+{
+  // get indent from the previous non-empty line
+  int line = begin.line();
+  int prevIndent = 0;
+  
+  while(line--) {
+    if( (prevIndent = doc->plainKateTextLine(line)->firstChar()) != -1) break;
+  }
+  
+  if(prevIndent < 0) prevIndent = 0;
+  else prevIndent = doc->plainKateTextLine(line)->cursorX(prevIndent, tabWidth);
+  
+  // now count the number of open and close tags on the previous (i.e. 
+  // just-entered) line
+  QString l = doc->plainKateTextLine(begin.line() - 1)->string();
+  int length = l.length(), offset = 0, numOpen = 0, numClose = 0;
+  
+  for(offset = 0; offset < length; ++numOpen) {
+    int match = openTag.search(l, offset);
+    if(match == -1) break;
+    offset = match + 1;
+  }
+  
+  for(offset = 0; offset < length; ++numClose) {
+    int match = closeTag.search(l, offset);
+    if(match == -1) break;
+    offset = match + 1;
+  }
+  
+  // special exception: if the previous line starts with a close tag,
+  // we want to align with it
+  if(startsWithCloseTag.search(l) != -1) --numClose;
+  
+  // calculate the new indent
+  int indent = prevIndent + (numOpen - numClose) * indentWidth;
+  if(indent < 0) indent = 0;
+    
+  // apply
+  QString filler = tabString (indent);
+  doc->insertText (begin.line(), 0, filler);
+  begin.setCol(filler.length());
+}
+
+void KateXmlIndent::processChar (QChar c)
+{
+  if(c != '/') return;
+
+  // only alter lines that start with a close element
+  KateView *view = doc->activeView();
+  if(startsWithCloseTag.search(
+    doc->plainKateTextLine(view->cursorLine())->string()
+  ) == -1) return;
+
+  // process it
+  processLine(view->cursorLine());
+}
+
+void KateXmlIndent::processLine (KateDocCursor &line)
+{
+  processLine (line.line());
+}
+
+void KateXmlIndent::processSection (KateDocCursor &begin, KateDocCursor &end)
+{
+  uint endLine = end.line();
+  for(uint line = begin.line(); line <= endLine; ++line) processLine(line);
+}
+
+void KateXmlIndent::processLine (uint line)
+{
+  KateTextLine::Ptr kateLine = doc->plainKateTextLine(line);
+
+  // compute new indent based on previous indent
+  uint prevIndent = 0, numOpen = 0;
+  if(line) findOpeningElemIndent(line - 1, prevIndent, numOpen);
+  uint indent = prevIndent + numOpen * indentWidth;
+  
+  // unindent lines that start with a close tag
+  if(indent) {
+    int firstChar = kateLine->firstChar();
+    if(kateLine->getChar(firstChar) == '<' && kateLine->getChar(firstChar + 1) == '/') {
+      if(indent > indentWidth) indent -= indentWidth;
+      else indent = 0;
+    }
+  }
+  
+  // apply new indent
+  doc->removeText(line, 0, line, kateLine->firstChar());
+  QString filler = tabString(indent);
+  if (indent > 0) doc->insertText(line, 0, filler);
+}
+
+void KateXmlIndent::findOpeningElemIndent (uint line, uint &indent, uint &numOpened)
+{
+  int depth = 1, pos;
+  KateTextLine::Ptr kateLine;
+  QString ln;
+  
+  indent = 0;
+  numOpened = 0;
+  
+  do {
+    kateLine = doc->plainKateTextLine(line);
+    ln = kateLine->string();
+    
+    pos = 0;
+    do {
+      
+      pos = openOrCloseTag.searchRev(ln, pos - 1);
+      if(pos == -1) break;
+      if(ln.at(pos + 1).unicode() == '/') {
+        // we found a closing tag
+        ++depth;
+      
+      } else {
+        // we found an opening tag
+        if(!--depth) {
+          // tag is unmatched
+          
+          // retrieve the indent of this line
+          indent = kateLine->cursorX(kateLine->firstChar(), tabWidth);
+
+          // count the number of (unclosed) open elements
+          for(int pos2 = -1; pos2 != pos; ) {
+            pos2 = openOrCloseTag.search(ln, pos2 + 1);
+            if(ln.at(pos2 + 1).unicode() == '/') {
+              if(numOpened) --numOpened;
+            } else {
+              ++numOpened;
+            }
+          }
+
+          return;
+        }
+      
+      }
+    
+    }while(pos);
+    
+  }while(line--);
+  
+  // reached start of document
+}
+
+// END
+
 // kate: space-indent on; indent-width 2; replace-tabs on;
diff -ru part/kateautoindent.h katepart-xmlindent/kateautoindent.h
--- part/kateautoindent.h	2004-06-22 18:36:33.000000000 +0100
+++ katepart-xmlindent/kateautoindent.h	2004-09-30 02:06:55.323830791 +0100
@@ -228,6 +228,33 @@
     static QRegExp blockBegin;
 };
 
+class KateXmlIndent : public KateAutoIndent
+{
+  public:
+    KateXmlIndent (KateDocument *doc);
+    ~KateXmlIndent ();
+    
+    virtual uint modeNumber () const { return KateDocumentConfig::imXmlStyle; }
+    virtual void processNewline (KateDocCursor &begin, bool needContinue);
+    virtual void processChar (QChar c);
+    virtual void processLine (KateDocCursor &line);
+    virtual bool canProcessLine() { return true; }
+    virtual void processSection (KateDocCursor &begin, KateDocCursor &end);
+    
+  private:
+    static QRegExp openTag;
+    static QRegExp closeTag;
+    static QRegExp startsWithCloseTag;
+    static QRegExp openOrCloseTag;
+    
+    // sets the indentation of a single line based on previous lines
+    void processLine (uint line);
+    
+    // returns the indentation of the last line with an opening element,
+    // plus the number of elements that were opened
+    void findOpeningElemIndent (uint line, uint &indent, uint &numOpened);
+};
+
 #endif
 
 // kate: space-indent on; indent-width 2; replace-tabs on;
diff -ru part/kateconfig.h katepart-xmlindent/kateconfig.h
--- part/kateconfig.h	2004-06-22 18:36:33.000000000 +0100
+++ katepart-xmlindent/kateconfig.h	2004-09-29 22:03:17.230472674 +0100
@@ -136,7 +136,8 @@
     {
       imNormal = 0,
       imCStyle = 1,
-      imPythonStyle = 2
+      imPythonStyle = 2,
+      imXmlStyle = 3
     };
 
     uint indentationMode () const;

[Attachment #8 (application/pgp-signature)]

_______________________________________________
KWrite-Devel mailing list
KWrite-Devel@kde.org
https://mail.kde.org/mailman/listinfo/kwrite-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic