Solved

Line Numbering and Syntax Highlighting in a Text Editor

Posted on 2004-04-14
4
3,030 Views
Last Modified: 2013-12-13
This question is a two part. First is we are trying to syntax highlighting to a text editor. The class we are using is listed below. This code works when we are creating a new file. However when we open an existing file it does not highlight any text. This code is specific to the syntax highlighting portion of our program. If you need to see any of the other classes please just request them.

import javax.swing.text.*;
import java.util.*;
import java.awt.*;
import javax.swing.text.AbstractDocument;

public class CodeDocument extends DefaultStyledDocument{
  private String word = "";
  private SimpleAttributeSet keyword = new SimpleAttributeSet();
  private SimpleAttributeSet string = new SimpleAttributeSet();
  private SimpleAttributeSet normal = new SimpleAttributeSet();
  private SimpleAttributeSet number = new SimpleAttributeSet();
  private SimpleAttributeSet comments = new SimpleAttributeSet();
  private int currentPos = 0;
  private Vector keywords = new Vector();
  public static int STRING_MODE = 10;
  public static int TEXT_MODE = 11;
  public static int NUMBER_MODE = 12;
  public static int COMMENT_MODE = 13;
  private int mode = TEXT_MODE;
 

  public CodeDocument() {
   //set the bold attribute
   StyleConstants.setBold(keyword, true);
   StyleConstants.setForeground(string, Color.magenta);
   StyleConstants.setForeground(number, Color.orange);
   StyleConstants.setForeground(comments, Color.blue);
   StyleConstants.setForeground(keyword,Color.red);
   StyleConstants.setItalic(comments, true);
  }
 

  private void insertKeyword(String str, int pos){
    try{
      //remove the old word and formatting
      this.remove(pos - str.length(), str.length());
      /*replace it with the same word, but new formatting
      *we MUST call the super class insertString method here, otherwise we
      *would end up in an infinite loop !!!!!*/
      super.insertString(pos - str.length(), str, keyword);
    }
    catch (Exception ex){
      ex.printStackTrace();
    }
  }
 

  private void insertTextString(String str, int pos){
    try{
      //remove the old word and formatting
      this.remove(pos,str.length());
      super.insertString(pos, str, string);
    }
    catch (Exception ex){
      ex.printStackTrace();
    }
  }
 

  private void insertNumberString(String str, int pos){
    try{
      //remove the old word and formatting
      this.remove(pos,str.length());
      super.insertString(pos, str, number);
    }
    catch (Exception ex){
      ex.printStackTrace();
    }
  }
 

  private void insertCommentString(String str, int pos){
    try{
      //remove the old word and formatting
      this.remove(pos,str.length());
      super.insertString(pos, str, comments);
    }
    catch (Exception ex){
      ex.printStackTrace();
    }
  }
 

  private void checkForString(){
    int offs = this.currentPos;
    Element element = this.getParagraphElement(offs);
    String elementText = "";
    try{
      //this gets our chuck of current text for the element we're on
      elementText = this.getText(element.getStartOffset(),
                                 element.getEndOffset() -
element.getStartOffset());
    }
    catch(Exception ex){
      //whoops!
      System.out.println("no text");
    }
    int strLen = elementText.length();
    if (strLen == 0) {return;}
    int i = 0;
 

    if (element.getStartOffset() > 0){
      //translates backward if neccessary
      offs = offs - element.getStartOffset();
    }
    int quoteCount = 0;
    if ((offs >= 0) && (offs <= strLen-1)){
      i = offs;
      while (i >0){
      //the while loop walks back until we hit a delimiter
 

        char charAt = elementText.charAt(i);
        if ((charAt == '"')){
         quoteCount ++;
        }
        i--;
      }
      int rem = quoteCount % 2;
      //System.out.println(rem);
      mode = (rem == 0) ? TEXT_MODE: STRING_MODE;
    }
  }
 

  private void checkForKeyword(){
    if (mode != TEXT_MODE) {
      return;
    }
    int offs = this.currentPos;
    Element element = this.getParagraphElement(offs);
    String elementText = "";
    try{
      //this gets our chuck of current text for the element we're on
      elementText = this.getText(element.getStartOffset(),
element.getEndOffset() - element.getStartOffset());
    }
    catch(Exception ex){
      //whoops!
      System.out.println("no text");
    }
    int strLen = elementText.length();
    if (strLen == 0) {return;}
    int i = 0;
 

    if (element.getStartOffset() > 0){
      //translates backward if neccessary
      offs = offs - element.getStartOffset();
    }
    if ((offs >= 0) && (offs <= strLen-1)){
      i = offs;
      while (i >0){
      //the while loop walks back until we hit a delimiter
        i--;
        char charAt = elementText.charAt(i);
        if ((charAt == ' ') | (i == 0) | (charAt =='(') | (charAt ==')') |
            (charAt == '{') | (charAt == '}')){ //if i == 0 then we're atthe begininng
          if(i != 0){
            i++;
          }
          word = elementText.substring(i, offs);//skip the period
 

          String s = word.trim().toLowerCase();
          //this is what actually checks for a matching keyword
          if (keywords.contains(s)){
            insertKeyword(word, currentPos);
          }
          break;
        }
      }
    }
  }
 

  private void checkForNumber(){
    int offs = this.currentPos;
    Element element = this.getParagraphElement(offs);
    String elementText = "";
    try{
      //this gets our chuck of current text for the element we're on
      elementText = this.getText(element.getStartOffset(),
element.getEndOffset() - element.getStartOffset());
    }
    catch(Exception ex){
      //whoops!
      System.out.println("no text");
    }
    int strLen = elementText.length();
    if (strLen == 0) {return;}
    int i = 0;
 

    if (element.getStartOffset() > 0){
      //translates backward if neccessary
      offs = offs - element.getStartOffset();
    }
    mode = TEXT_MODE;
    if ((offs >= 0) && (offs <= strLen-1)){
      i = offs;
      while (i >0){
      //the while loop walks back until we hit a delimiter
        char charAt = elementText.charAt(i);
        if ((charAt == ' ') | (i == 0) | (charAt =='(') | (charAt ==')') |
            (charAt == '{') | (charAt == '}') /*|*/){ //if i == 0 then we're at the begininng
          if(i != 0){
            i++;
          }
          mode = NUMBER_MODE;
          break;
        }
        else if (!(charAt >= '0' & charAt <= '9' | charAt=='.'
                  | charAt=='+' | charAt=='-'
                  | charAt=='/' | charAt=='*'| charAt=='%' | charAt=='=')){
          mode = TEXT_MODE;
          break;
        }
        i--;
      }
    }
  }
 

  private void checkForComment(){
    int offs = this.currentPos;
    Element element = this.getParagraphElement(offs);
    String elementText = "";
    try{
      //this gets our chuck of current text for the element we're on
      elementText = this.getText(element.getStartOffset(),
element.getEndOffset() - element.getStartOffset());
    }
    catch(Exception ex){
      //whoops!
      System.out.println("no text");
    }
    int strLen = elementText.length();
    if (strLen == 0) {return;}
    int i = 0;
 

    if (element.getStartOffset() > 0){
      //translates backward if neccessary
      offs = offs - element.getStartOffset();
    }
    if ((offs >= 1) && (offs <= strLen-1)){
      i = offs;
      char commentStartChar1 = elementText.charAt(i-1);
      char commentStartChar2 = elementText.charAt(i);
      if ((commentStartChar1 == '/' && commentStartChar2 == '*')){
          mode = COMMENT_MODE;
          this.insertCommentString("/*", currentPos-1);
      }
      else if (commentStartChar1 == '*' && commentStartChar2 == '/'){
          mode = TEXT_MODE;
          this.insertCommentString("*/", currentPos-1);
      }
    }
  }
 

  private void processChar(String str){
    char strChar = str.charAt(0);
    if (mode != this.COMMENT_MODE){
      mode = TEXT_MODE;
    }
      switch (strChar){
        case ('{'):case ('}'):case (' '): case('\n'):
        case ('('):case (')'):case (';'):case ('.'):{
          checkForKeyword();
          if (mode == STRING_MODE && strChar == '\n'){
            mode = TEXT_MODE;
          }
        }
        break;
        case ('"'):{
          insertTextString(str, currentPos);
          this.checkForString();
        }
        break;
        case ('0'):case ('1'):case ('2'):case ('3'):case ('4'):
        case ('5'):case ('6'):case ('7'):case ('8'):case ('9'):{
          checkForNumber();
        }
        break;
        case ('*'):case ('/'):{
          checkForComment();
        }
        break;
      }
      if (mode == this.TEXT_MODE){
        this.checkForString();
      }
      if (mode == this.STRING_MODE){
        insertTextString(str, this.currentPos);
      }
      else if (mode == this.NUMBER_MODE){
        insertNumberString(str, this.currentPos);
      }
      else if (mode == this.COMMENT_MODE){
        insertCommentString(str, this.currentPos);
      }
 

  }
 

  private void processChar(char strChar){
      char[] chrstr = new char[1];
      chrstr[0] = strChar;
      String str = new String(chrstr);
      processChar(str);
  }
 

  public void insertString(int offs,
                          String str,
                          AttributeSet a) throws BadLocationException{
    super.insertString(offs, str, normal);
 

    int strLen = str.length();
    int endpos = offs + strLen;
    int strpos;
    for (int i=offs;i<endpos;i++){
      currentPos = i;
      strpos = i - offs;
      processChar(str.charAt(strpos));
    }
    currentPos = offs;
  }
 

  public Vector getKeywords(){
    return this.keywords;
  }
 

  public void setKeywords(Vector aKeywordList){
    if (aKeywordList != null){
      this.keywords = aKeywordList;
    }
  }
}


The second part of my question is line numbering in the editor that we are creating. For new documents it appears fine. If we open an existing document then the code overlaps the line numbers. The class file that we are using to do line numbering is below. If you need to see any other files please request them and they will be provided.


import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;

class LNTextPane extends JFrame
{
    public LNTextPane(JTextPane edit)
    {
    System.out.println("inside constructor ");
    edit.setEditorKit(new NumberedEditorKit());
      
    }
}
      class NumberedEditorKit extends StyledEditorKit
      {
          public ViewFactory getViewFactory()
          {
                System.out.println("inside getView factory");
              return new NumberedViewFactory();
              
          }
      }

      class NumberedViewFactory implements ViewFactory
      {
          public View create(Element elem)
          {
              System.out.println("inside create");
              String kind = elem.getName();
              if (kind != null)
                  if (kind.equals(AbstractDocument.ContentElementName)) {
                      return new LabelView(elem);
                  }
                  else if (kind.equals(AbstractDocument.
                                   ParagraphElementName)) {
                  //    return new ParagraphView(elem);
                      return new NumberedParagraphView(elem);
                  }
                  else if (kind.equals(AbstractDocument.
                           SectionElementName)) {
                      return new BoxView(elem, View.Y_AXIS);
                  }
                  else if (kind.equals(StyleConstants.
                           ComponentElementName)) {
                      return new ComponentView(elem);
                  }
                  else if (kind.equals(StyleConstants.IconElementName)) {
                      return new IconView(elem);
                  }
              // default to text display
              return new LabelView(elem);
          }
      }

      class NumberedParagraphView extends ParagraphView
      {
          public short NUMBERS_WIDTH=25;
      
          public NumberedParagraphView(Element e)
          {
              super(e);
              short top = 0;
              short left = 0;
              short bottom = 0;
              short right = 0;
              this.setInsets(top, left,bottom,right);
          }
      
          protected void setInsets(short top, short left, short bottom,short right)
          {
                System.out.println("inside Insets");
                super.setInsets(top,(short)(left+NUMBERS_WIDTH),bottom,right);
          }
      
          public void paintChild(Graphics g, Rectangle r, int n)
          {
              super.paintChild(g, r, n);
              int previousLineCount = getPreviousLineCount();
              int numberX = r.x - getLeftInset();
              int numberY = r.y + r.height - 5;
              g.drawString(Integer.toString(previousLineCount + n + 1),numberX, numberY);
          }
      
          public int getPreviousLineCount()
          {
              int lineCount = 0;
              View parent = this.getParent();
              int count = parent.getViewCount();
              for (int i = 0; i < count; i++) {
                  if (parent.getView(i) == this) {
                      break;
                  }
                  else {
                      lineCount += parent.getView(i).getViewCount();
                  }
              }
              return lineCount;
          }
      }

0
Comment
Question by:rooster_0429
4 Comments
 
LVL 4

Expert Comment

by:john-at-7fff
ID: 10825335
Whew. Those are toughies. Good luck.

I've tried to write a custom view for line numbering, but it turned out that it was easier to set a RowHeaderView on a scrollpane. But you may be locked into your custom editor kit / custom view strategy. I feel your pain.

In any case, the RowHeaderView idea looks a bit like this:

(textPane here is actually a JTextArea)

final JScrollPane scrollPane = new JScrollPane(textPane);
rowHeader = new JLabel() {
    public Dimension getPreferredSize() {
        return new Dimension(rhWidth + 10, (int) textPane.getPreferredSize().getHeight());
    }
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setFont(tpFont);
        int i = h - tpFontMetrics.getDescent();
        int row = 1;
        while (i < getHeight()) {
        String s = Integer.toString(row);
        g.drawString(s, 5 + rhWidth - tpFontMetrics.stringWidth(s), i);
        i += h;
        row++;
    }
}
};
scrollPane.setRowHeaderView(rowHeader);

0
 
LVL 92

Expert Comment

by:objects
ID: 10827908
Grab the source for jedit and see what techniques it uses:
http://www.jedit.org/
0
 
LVL 1

Accepted Solution

by:
GhostMod earned 0 total points
ID: 10881763
PAQd, 500 points refunded.

GhostMod
Community Support Moderator
0

Featured Post

Gigs: Get Your Project Delivered by an Expert

Select from freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely and get projects done right.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
firstswap challenge 20 77
couple of eclipse 5 35
How to determine if a string is a valid SHA value 7 28
going to wrong jsp page 2 19
Programmer's Notepad is, one of the best free text editing tools available, simply because the developers appear to have second-guessed every weird problem or issue a programmer is likely to run into. One of these problems is selecting and deleti…
How to install Selenium IDE and loops for quick automated testing. Get Selenium IDE from http://seleniumhq.org Go to that link and select download selenium in the right hand columnThat will then direct you to their download page.From that page s…
Viewers will learn about arithmetic and Boolean expressions in Java and the logical operators used to create Boolean expressions. We will cover the symbols used for arithmetic expressions and define each logical operator and how to use them in Boole…
This theoretical tutorial explains exceptions, reasons for exceptions, different categories of exception and exception hierarchy.

813 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now