【插件开发】—— 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: ISourceViewer
Configures 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 ISourceViewer
Parameters:
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 cycle
12 */
13 public class Activator extends AbstractUIPlugin {
14
15 // The plug-in ID
16 public static final String PLUGIN_ID = "TestPreference";
17
18 // for SQL editor
19 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 instance
27 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 }
2 Action触发对话框
1 doubleClickAction = new Action() {
2 public void run() {
3 DDLEditDialog dialog = new DDLEditDialog(viewer.getControl().getShell());
4 dialog.open();
5 }
6 };
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 Administrator
136 *
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 Administrator
179 *
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 }
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 }
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 extends
11 AbstractPreferenceInitializer {
12
13 public AbstractPreferenceInitializer1() {
14 // TODO Auto-generated constructor stub
15 }
16
17 @Override
18 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 }
由于博主自己对这部分的代码也没有达到熟练使用的地步,因此编码的过程有些混乱,这里还需要多加练习和实践,才能领会其中的妙处!本文也仅仅是作为一个入门而已。
- 创建用户时的密码校验问题(r2第34天)
- 巧用xmltype解析clob数据(r2笔记33天)
- R语言-中国各城市PM2.5数据间的相关分析
- 妹子如何运用R语言数据分析选择心仪的对象?
- 生产环境大型sql语句调优实战第一篇(二) (r2笔记32天)
- 生产环境大型sql语句调优实战第一篇(一) (r2笔记第31天)
- 讨论 | 你是否遇到过你完全不能理解的机器学习概念?
- 【专业技术】C++里面重要的几个关键字的用法
- sql monitor的使用(一) (r2第30天)
- sql_profile的使用(一) (r2笔记29天)
- 关于权限设置的一个小把戏(r2第27天)
- R语言实现混合模型
- 数据迁移判断非空约束(r2笔记45天)
- 【专业技术】linux下socket编程
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 设计模式~观察者模式
- 网页无插件视频流媒体播放器EasyPlayerPro-IOS版如何解决有声音无画面的问题?
- (建议收藏)Java基础知识笔记二(详细)
- Android 手机如何拍摄RAW图
- 「干货」基本数据类型和引用数据类型的区别
- int 和 integer :装箱和拆箱的过程,会用到什么方法,你觉得这个会对性能有影响吗,原因是什么(百度一面)
- 数组:这个循环可以转懵很多人!
- 企业远程办公视频会议系统EasyRTC-SFU下侧边栏边框超限问题如何解决?
- 编写高质量可维护的代码:数据建模
- 新版企业远程办公视频通话系统EasyRTC-SFU,如何解决用户登录信息更新不及时的问题?
- 服务应用突然宕机了?别怕,Dubbo 帮你自动搞定服务隔离!
- 33.Python字符串方法find以及与序列解包的技巧结合
- 代码审计从0到1 —— Centreon One-click To RCE
- 一文带你深扒ClassLoader内核,揭开它的神秘面纱!
- 小知识:OGG的TRANLOGOPTIONS MINEFROMACTIVEDG参数