首页 > 代码库 > 【插件开发】—— 9 编辑器代码分块着色-高亮显示!

【插件开发】—— 9 编辑器代码分块着色-高亮显示!

前文回顾:

1 插件学习篇

2 简单的建立插件工程以及模型文件分析

3 利用扩展点,开发透视图

4 SWT编程须知

5 SWT简单控件的使用与布局搭配

6 SWT复杂空间与布局搭配

7 SWT布局详解

8 IPreferenceStore使用详解

  这篇讲解依然是一个重头的技巧,就是【代码的着色】。大家在使用各种编辑器的时候都会发现,有些关键词和一些注释之类的都会以不同的颜色进行显示,那么它是怎么做到呢?先看一下示例的运行效果!

  凭空思考下:

  【IO书写】首先这些输入的东西可能是以一些字符串的形式进行书写。

  【分词分块】然后必然经过分词,把他们分成一块一块的。

  【着色】这样之后扫描每个分块进行分类,不同的分类显示不同的颜色!

 

  大体上是这样一个过程,那么Eclipse是怎样做到的呢?

 

  SourceViewer!—— 代码编写的视图窗口

  这里主要是用了一个特殊的view模型:SourceViewer,它是一种特殊的文本视图,让我们可以配置自己的代码显示规则!看一下官方的API

SourceViewer(Composite parent, IVerticalRuler ruler, int styles) //Constructs a new source viewer.

  这里第一个跟第三个参数都跟普通的Control控件差不多。

  中间的参数用于设置代码的一个垂直规则(其实就是编辑器左边和右边有提示效果的垂直边栏),想了解的话可以参考它的官方API。

  使用方法如下:

VerticalRuler(int width) //Constructs a vertical ruler with the given width.

  如果不想有其他的配置,可以设置它的宽度为0。

  接下来需要设置它的配置对象,用于对着色,分词等信息进行配置。

public void configure(SourceViewerConfiguration configuration);/*Description copied from interface: ISourceViewerConfigures the source viewer using the given configuration. Prior to 3.0 this method can only be called once. Since 3.0 this method can be called again after a call to ISourceViewerExtension2.unconfigure().Specified by:configure in interface ISourceViewerParameters:configuration - the source viewer configuration to be used*/

  Document!—— 代码文档,提供切分分块等操作.

  这个文档对象需要我们提供一个分块对象,对输入的文件流进行分块。这里主要使用一个接口IDocumentPartitioner,常用的实现类是FastPartitioner。

public FastPartitioner(IPartitionTokenScanner scanner, String[] legalContentTypes)

  第一个参数是一个扫描对象,第二个参数用于分块的规则。

 

  实现过程

 

  抽象的设想大概如上面所述,但是做起来还是很困难,虽然知道了用什么类或者方法,但是如何组织才是最大的困难!这里借助一个开源源码,书写SQL语句的编辑器,来讲解一下代码着色的主要过程!

  我们要解决的问题大致如下:

  如何进行分块?

  如何进行着色?

  如何附加到编辑器上?

  一下是代码编写的思维导图

  1 创建SQL对应的SourceViewer的配置类SQLConfiguration

  所有自定仪的配置类都要继承Eclipse的SourceViewerConfiguration类。

  需要重写的类大致有如下几个:

  1.1 getConfiguredContentTypes 这个方法用于返回一个数组,这个数组规定了需要进行处理的类型,当遇到这种类型匹配的分块时,就会进行响应的处理。这里的业务场景是这样:我们编写SQL语句,相应进行处理着色的应该是关键字、字符串、注释。其他的输入对象我们就不需要进行处理了。下面便是返回的三种类型标识。

public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) {            return new String[] { IDocument.DEFAULT_CONTENT_TYPE,                    SQLPartitionScanner.SQL_COMMENT,                    SQLPartitionScanner.SQL_STRING };        }

  1.2 接下来是对应的三种扫描器

      private RuleBasedScanner getCommentScanner(){//注释扫描器            RuleBasedScanner scanner = new RuleBasedScanner();            EditorColorProvider colorProvider = Activator.getDefault().getEditorColorProvider();            scanner.setDefaultReturnToken(                    colorProvider.getToken(Activator.PREF_COLOR_COMMENT));            return scanner;        }        private RuleBasedScanner getStringScanner(){//字符串扫描器            RuleBasedScanner scanner = new RuleBasedScanner();            EditorColorProvider colorProvider = Activator.getDefault().getEditorColorProvider();            scanner.setDefaultReturnToken(                    colorProvider.getToken(Activator.PREF_COLOR_STRING));            return scanner;        }        private RuleBasedScanner getDefaultScanner(){//默认关键字扫描器            return new SQLKeywordPartitionScanner();        }

  这里根据自定义编写我们自己的关键字扫描器

public static class SQLKeywordPartitionScanner extends RuleBasedScanner {        private static String[] KEYWORDS = {"select", "update", "insert",            "into", "delete", "from", "where", "values", "set", "order", "by",            "left", "outer", "join", "having", "group", "create", "alter", "drop", "table"};        public SQLKeywordPartitionScanner(){            IToken keyword = Activator.getDefault().getEditorColorProvider().getToken(                    Activator.PREF_COLOR_KEYWORD);            IToken other = Activator.getDefault().getEditorColorProvider().getToken(                    Activator.PREF_COLOR_DEFAULT);            WordRule wordRule = new WordRule(new IWordDetector() {                public boolean isWordPart(char c) {                    return Character.isJavaIdentifierPart(c);                }                public boolean isWordStart(char c) {                    return Character.isJavaIdentifierStart(c);                }            }, other);            for (int i = 0; i < KEYWORDS.length; i++) {                wordRule.addWord(KEYWORDS[i], keyword);                wordRule.addWord(KEYWORDS[i].toUpperCase(), keyword);            }            IRule[] rules = new IRule[2];            rules[0] = wordRule;            rules[1] = new WhitespaceRule(new IWhitespaceDetector() {                public boolean isWhitespace(char character) {                    return Character.isWhitespace(character);                }            });            setRules(rules);        }    }

  1.3 getPresentationReconciler 是源码视图使用的表现协调器,翻译的比较蹩脚,其实就是每一种类型的分块如何展现!

public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {            PresentationReconciler reconciler = new PresentationReconciler();            DefaultDamagerRepairer commentDR = new DefaultDamagerRepairer(getCommentScanner());            reconciler.setDamager(commentDR, SQLPartitionScanner.SQL_COMMENT);            reconciler.setRepairer(commentDR, SQLPartitionScanner.SQL_COMMENT);            DefaultDamagerRepairer stringDR = new DefaultDamagerRepairer(getStringScanner());            reconciler.setDamager(stringDR, SQLPartitionScanner.SQL_STRING);            reconciler.setRepairer(stringDR, SQLPartitionScanner.SQL_STRING);            DefaultDamagerRepairer keywordDR = new DefaultDamagerRepairer(getDefaultScanner());            reconciler.setDamager(keywordDR, IDocument.DEFAULT_CONTENT_TYPE);            reconciler.setRepairer(keywordDR, IDocument.DEFAULT_CONTENT_TYPE);            return reconciler;        }

  1.4 着色方法EditorColorProvider,提供对不同的类型显示不同的颜色

  这个类提供了一个map,里面包含了对应的类型及其对应的RGB颜色的Color对象,通过查询这个map,可以获取相应的颜色,进行着色。

public IToken getToken(String prefKey){       Token token = (Token) tokenTable.get(prefKey);       if (token == null){          String colorName = store.getString(prefKey);          RGB rgb = StringConverter.asRGB(colorName);          token = new Token(new TextAttribute(getColor(rgb)));          tokenTable.put(prefKey, token);       }       return token;    }

  关于这里面的store,是前一篇讲解过的插件初始化设定的参数。用法可以参考前一篇帖子,这里贴出initializer类中的实现:

       store.setDefault(Activator.PREF_COLOR_DEFAULT, StringConverter.asString(new RGB(0,0,0)));        store.setDefault(Activator.PREF_COLOR_COMMENT, StringConverter.asString(new RGB(0,128,0)));        store.setDefault(Activator.PREF_COLOR_STRING, StringConverter.asString(new RGB(0,0,255)));        store.setDefault(Activator.PREF_COLOR_KEYWORD, StringConverter.asString(new RGB(128,0,128)));

 

  2 创建分块扫描器

  这里是针对注释以及字符串进行分块。

  需要在够咱函数中创建一个分块规则:IPredicateRule 数组。具体规则的参数可以参考下面的参数。

public class SQLPartitionScanner extends RuleBasedPartitionScanner {                public static final String SQL_COMMENT = "__sql_comment";        public static final String SQL_STRING = "__sql_string";        public SQLPartitionScanner() {            IPredicateRule[] rules = new IPredicateRule[4];            IToken comment = new Token(SQL_COMMENT);            rules[0] = new MultiLineRule("/*", "*/", comment, (char) 0, true);
/*
startSequence the pattern‘s start sequence
endSequence the pattern‘s end sequence
token the token to be returned on success
escapeCharacter the escape character
breaksOnEOF indicates whether the end of the file terminates this rule successfully
*/ rules[
1] = new EndOfLineRule("--", comment);/*
startSequence the pattern‘s start sequence
token the token to be returned on success
*/ IToken
string = new Token(SQL_STRING); rules[2] = new SingleLineRule("\"", "\"", string, \\); rules[3] = new SingleLineRule("\‘", "\‘", string, \\);/*
startSequence the pattern‘s start sequence
endSequence the pattern‘s end sequence
token the token to be returned on success
escapeCharacter the escape character
*/ setPredicateRules(rules); } }

  

  3 这样基本上需要的准备工作就做完了,接下来要进行使用了。

  首先在合适的位置触发编辑对话框的弹出!

DDLEditDialog dialog = new DDLEditDialog(viewer.getControl().getShell());                dialog.open();

  设定SQL配置类,以及分块扫描器

SourceViewer sqlEditor = new SourceViewer(parent, new VerticalRuler(0), SWT.V_SCROLL | SWT.H_SCROLL);        //设置配置项        sqlEditor.configure(new SQLConfiguration());                sqlEditor.getTextWidget().setFont(JFaceResources.getTextFont());        Document document = new Document();
     //设置分块扫描器 IDocumentPartitioner partitioner
= new FastPartitioner( new SQLPartitionScanner(), new String[] { SQLPartitionScanner.SQL_COMMENT, SQLPartitionScanner.SQL_STRING }); partitioner.connect(document); document.setDocumentPartitioner(partitioner); sqlEditor.setDocument(document); sqlEditor.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); StyledText text = sqlEditor.getTextWidget();

 

  全部代码

  源码ZIP包下载:源码插件下载

  使用方法:

  1 打开视图SampleView

  2 双击下面的任意一行!

  3 弹出对话框进行编辑!

  1 Activator插件启动类

 1 package testpreference; 2  3 import org.eclipse.jface.preference.IPreferenceStore; 4 import org.eclipse.jface.resource.ImageDescriptor; 5 import org.eclipse.ui.plugin.AbstractUIPlugin; 6 import org.osgi.framework.BundleContext; 7  8 import testpreference.dialog.EditorColorProvider; 9 10 /**11  * The activator class controls the plug-in life cycle12  */13 public class Activator extends AbstractUIPlugin {14 15     // The plug-in ID16     public static final String PLUGIN_ID = "TestPreference";17 18     // for SQL editor19     public static final String PREF_COLOR_DEFAULT = "colorDefault";20     public static final String PREF_COLOR_COMMENT = "colorComment";21     public static final String PREF_COLOR_STRING = "colorString";22     public static final String PREF_COLOR_KEYWORD = "colorKeyword";23     24     private IPreferenceStore store;25     private EditorColorProvider colorProvider;26     // The shared instance27     private static Activator plugin;28 29     public Activator() {30     }31 32     public void start(BundleContext context) throws Exception {33         super.start(context);34         plugin = this;35         this.colorProvider = new EditorColorProvider(getPreferenceStore());36     }37     38     public EditorColorProvider getEditorColorProvider(){39         return this.colorProvider;40     }41 42 ...43 44     public static Activator getDefault() {45         return plugin;46     }47 }
View Code

  2 Action触发对话框

1 doubleClickAction = new Action() {2             public void run() {3                 DDLEditDialog dialog = new DDLEditDialog(viewer.getControl().getShell());4                 dialog.open();5             }6         };
View Code

  3 对话框实现类

  1 package testpreference.dialog;  2   3 import org.eclipse.jface.dialogs.Dialog;  4 import org.eclipse.jface.dialogs.IDialogConstants;  5 import org.eclipse.jface.resource.JFaceResources;  6 import org.eclipse.jface.text.Document;  7 import org.eclipse.jface.text.IDocument;  8 import org.eclipse.jface.text.IDocumentPartitioner;  9 import org.eclipse.jface.text.presentation.IPresentationReconciler; 10 import org.eclipse.jface.text.presentation.PresentationReconciler; 11 import org.eclipse.jface.text.rules.DefaultDamagerRepairer; 12 import org.eclipse.jface.text.rules.EndOfLineRule; 13 import org.eclipse.jface.text.rules.FastPartitioner; 14 import org.eclipse.jface.text.rules.IPredicateRule; 15 import org.eclipse.jface.text.rules.IRule; 16 import org.eclipse.jface.text.rules.IToken; 17 import org.eclipse.jface.text.rules.IWhitespaceDetector; 18 import org.eclipse.jface.text.rules.IWordDetector; 19 import org.eclipse.jface.text.rules.MultiLineRule; 20 import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; 21 import org.eclipse.jface.text.rules.RuleBasedScanner; 22 import org.eclipse.jface.text.rules.SingleLineRule; 23 import org.eclipse.jface.text.rules.Token; 24 import org.eclipse.jface.text.rules.WhitespaceRule; 25 import org.eclipse.jface.text.rules.WordRule; 26 import org.eclipse.jface.text.source.ISourceViewer; 27 import org.eclipse.jface.text.source.SourceViewer; 28 import org.eclipse.jface.text.source.SourceViewerConfiguration; 29 import org.eclipse.jface.text.source.VerticalRuler; 30 import org.eclipse.swt.SWT; 31 import org.eclipse.swt.custom.StyledText; 32 import org.eclipse.swt.graphics.Point; 33 import org.eclipse.swt.layout.GridData; 34 import org.eclipse.swt.widgets.Composite; 35 import org.eclipse.swt.widgets.Control; 36 import org.eclipse.swt.widgets.Shell; 37  38 import testpreference.Activator; 39  40 public class DDLEditDialog extends Dialog{ 41  42     public DDLEditDialog(Shell parent) { 43         super(parent); 44         setShellStyle(getShellStyle()|SWT.RESIZE); 45     } 46  47     protected Point getInitialSize() { 48         return new Point(600, 450); 49     } 50  51     protected Control createDialogArea(Composite parent) { 52         getShell().setText("DDL"); 53  54         SourceViewer sqlEditor = new SourceViewer(parent, new VerticalRuler(0), SWT.V_SCROLL | SWT.H_SCROLL); 55         //设置配置项 56         sqlEditor.configure(new SQLConfiguration()); 57          58         sqlEditor.getTextWidget().setFont(JFaceResources.getTextFont()); 59  60         Document document = new Document(); 61         IDocumentPartitioner partitioner = new FastPartitioner( 62                 new SQLPartitionScanner(), 63                 new String[] { 64                     SQLPartitionScanner.SQL_COMMENT, 65                     SQLPartitionScanner.SQL_STRING 66                 }); 67         partitioner.connect(document); 68         document.setDocumentPartitioner(partitioner); 69         sqlEditor.setDocument(document); 70         sqlEditor.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); 71  72         StyledText text = sqlEditor.getTextWidget(); 73  74         return text; 75     } 76  77     protected void createButtonsForButtonBar(Composite parent) { 78         createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); 79     } 80      81     public class SQLConfiguration extends SourceViewerConfiguration { 82  83         public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) { 84             return new String[] { IDocument.DEFAULT_CONTENT_TYPE, 85                     SQLPartitionScanner.SQL_COMMENT, 86                     SQLPartitionScanner.SQL_STRING }; 87         } 88  89         private RuleBasedScanner getCommentScanner(){ 90             RuleBasedScanner scanner = new RuleBasedScanner(); 91             EditorColorProvider colorProvider = Activator.getDefault().getEditorColorProvider(); 92             scanner.setDefaultReturnToken( 93                     colorProvider.getToken(Activator.PREF_COLOR_COMMENT)); 94             return scanner; 95         } 96  97         private RuleBasedScanner getStringScanner(){ 98             RuleBasedScanner scanner = new RuleBasedScanner(); 99             EditorColorProvider colorProvider = Activator.getDefault().getEditorColorProvider();100             scanner.setDefaultReturnToken(101                     colorProvider.getToken(Activator.PREF_COLOR_STRING));102             return scanner;103         }104 105         private RuleBasedScanner getDefaultScanner(){106             return new SQLKeywordPartitionScanner();107         }108 109 110         public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {111 112             PresentationReconciler reconciler = new PresentationReconciler();113 114             DefaultDamagerRepairer commentDR = new DefaultDamagerRepairer(getCommentScanner());115             reconciler.setDamager(commentDR, SQLPartitionScanner.SQL_COMMENT);116             reconciler.setRepairer(commentDR, SQLPartitionScanner.SQL_COMMENT);117 118             DefaultDamagerRepairer stringDR = new DefaultDamagerRepairer(getStringScanner());119             reconciler.setDamager(stringDR, SQLPartitionScanner.SQL_STRING);120             reconciler.setRepairer(stringDR, SQLPartitionScanner.SQL_STRING);121 122             DefaultDamagerRepairer keywordDR = new DefaultDamagerRepairer(getDefaultScanner());123             reconciler.setDamager(keywordDR, IDocument.DEFAULT_CONTENT_TYPE);124             reconciler.setRepairer(keywordDR, IDocument.DEFAULT_CONTENT_TYPE);125 126             return reconciler;127         }128     }129     130     131     132     133     /**134      * 关键词分词135      * @author Administrator136      *137      */138     public static class SQLKeywordPartitionScanner extends RuleBasedScanner {139 140         private static String[] KEYWORDS = {"select", "update", "insert",141             "into", "delete", "from", "where", "values", "set", "order", "by",142             "left", "outer", "join", "having", "group", "create", "alter", "drop", "table"};143 144         public SQLKeywordPartitionScanner(){145             IToken keyword = Activator.getDefault().getEditorColorProvider().getToken(146                     Activator.PREF_COLOR_KEYWORD);147             IToken other = Activator.getDefault().getEditorColorProvider().getToken(148                     Activator.PREF_COLOR_DEFAULT);149 150             WordRule wordRule = new WordRule(new IWordDetector() {151                 public boolean isWordPart(char c) {152                     return Character.isJavaIdentifierPart(c);153                 }154 155                 public boolean isWordStart(char c) {156                     return Character.isJavaIdentifierStart(c);157                 }158             }, other);159             for (int i = 0; i < KEYWORDS.length; i++) {160                 wordRule.addWord(KEYWORDS[i], keyword);161                 wordRule.addWord(KEYWORDS[i].toUpperCase(), keyword);162             }163             IRule[] rules = new IRule[2];164             rules[0] = wordRule;165             rules[1] = new WhitespaceRule(new IWhitespaceDetector() {166                 public boolean isWhitespace(char character) {167                     return Character.isWhitespace(character);168                 }169             });170 171             setRules(rules);172         }173 174     }175     176     /**177      * 用于SQL编辑分区,区分字符串或者注释178      * @author Administrator179      *180      */181     public class SQLPartitionScanner extends RuleBasedPartitionScanner {182         183         public static final String SQL_COMMENT = "__sql_comment";184         public static final String SQL_STRING = "__sql_string";185 186         public SQLPartitionScanner() {187             IPredicateRule[] rules = new IPredicateRule[4];188 189             IToken comment = new Token(SQL_COMMENT);190             rules[0] = new MultiLineRule("/*", "*/", comment, (char) 0, true);191             rules[1] = new EndOfLineRule("--", comment);192 193             IToken string = new Token(SQL_STRING);194             rules[2] = new SingleLineRule("\"", "\"", string, \\);195             rules[3] = new SingleLineRule("\‘", "\‘", string, \\);196 197             setPredicateRules(rules);198         }199         200     }201 }
View Code

  4 颜色提供类

 1 package testpreference.dialog; 2  3 import java.util.HashMap; 4 import java.util.Iterator; 5 import java.util.Map; 6  7 import org.eclipse.jface.preference.IPreferenceStore; 8 import org.eclipse.jface.resource.StringConverter; 9 import org.eclipse.jface.text.TextAttribute;10 import org.eclipse.jface.text.rules.IToken;11 import org.eclipse.jface.text.rules.Token;12 import org.eclipse.jface.util.PropertyChangeEvent;13 import org.eclipse.swt.graphics.Color;14 import org.eclipse.swt.graphics.RGB;15 import org.eclipse.swt.widgets.Display;16 17 public class EditorColorProvider {18 19     private Map<RGB, Color> colorTable = new HashMap<RGB, Color>(10);20     private Map<String, IToken> tokenTable = new HashMap<String, IToken>(10);21     IPreferenceStore store;22 23     public EditorColorProvider(IPreferenceStore store) {24         this.store = store;25     }26 27     public IToken getToken(String prefKey){28        Token token = (Token) tokenTable.get(prefKey);29        if (token == null){30           String colorName = store.getString(prefKey);31           RGB rgb = StringConverter.asRGB(colorName);32           token = new Token(new TextAttribute(getColor(rgb)));33           tokenTable.put(prefKey, token);34        }35        return token;36     }37 38     public void dispose(){39         Iterator<Color> e = colorTable.values().iterator();40         while (e.hasNext()){41             e.next().dispose();42         }43     }44 45     public Color getColor(String prefKey){46           String colorName = store.getString(prefKey);47           RGB rgb = StringConverter.asRGB(colorName);48           return getColor(rgb);49     }50 51     private Color getColor(RGB rgb) {52         Color color = (Color) colorTable.get(rgb);53         if (color == null){54            color = new Color(Display.getCurrent(), rgb);55            colorTable.put(rgb, color);56         }57         return color;58     }59 60     public boolean affectsTextPresentation(PropertyChangeEvent event){61        Token token = (Token) tokenTable.get(event.getProperty());62        return (token != null);63     }64 65     public void handlePreferenceStoreChanged(PropertyChangeEvent event){66        String prefKey = event.getProperty();67        Token token = (Token) tokenTable.get(prefKey);68        if (token != null){69           String colorName = store.getString(prefKey);70           RGB rgb = StringConverter.asRGB(colorName);71           token.setData(new TextAttribute(getColor(rgb)));72        }73     }74 }
View Code

  5 preferenceStore设置初始化参数

 1 package testpreference.preference; 2  3 import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; 4 import org.eclipse.jface.preference.IPreferenceStore; 5 import org.eclipse.jface.resource.StringConverter; 6 import org.eclipse.swt.graphics.RGB; 7  8 import testpreference.Activator; 9 10 public class AbstractPreferenceInitializer1 extends11         AbstractPreferenceInitializer {12 13     public AbstractPreferenceInitializer1() {14         // TODO Auto-generated constructor stub15     }16 17     @Override18     public void initializeDefaultPreferences() {19         IPreferenceStore store = Activator.getDefault().getPreferenceStore();20 21 //        store.setDefault(Activator.PREF_PARAM_1, "hello");22 //        store.setDefault(Activator.PREF_PARAM_2, "xingoo");23         24         store.setDefault(Activator.PREF_COLOR_DEFAULT, StringConverter.asString(new RGB(0,0,0)));25         store.setDefault(Activator.PREF_COLOR_COMMENT, StringConverter.asString(new RGB(0,128,0)));26         store.setDefault(Activator.PREF_COLOR_STRING, StringConverter.asString(new RGB(0,0,255)));27         store.setDefault(Activator.PREF_COLOR_KEYWORD, StringConverter.asString(new RGB(128,0,128)));28     }29 }
View Code

 

  由于博主自己对这部分的代码也没有达到熟练使用的地步,因此编码的过程有些混乱,这里还需要多加练习和实践,才能领会其中的妙处!本文也仅仅是作为一个入门而已。

【插件开发】—— 9 编辑器代码分块着色-高亮显示!