使用Visual Studio SDK制作GLSL词法着色插件
使用Visual Studio SDK制作GLSL词法着色插件
我们在Visual Studio上开发OpenGL ES项目时,避免不了写Shader。这时在vs里直接编辑shader就会显得很方便。但是vs默认是不支持GLSL的语法着色的,我们只好自己动手创造。最简单的实现自定义语法着色的方法就是创建一个VSIX插件包,我们只需要安装Visual Studio SDK,使用内置的模版就可以构建一个插件项目。
1. 安装Visual Studio SDK
在http://www.microsoft.com/en-us/download/details.aspx?id=40758下载最新的Visual Studio 2013 SDK。
双击安装,一路next即可。
安装完毕后我们可以在新建项目->模版->C#中看到“扩展性”这一条目,这些就是开发插件用的模版了。
2. 创建插件项目
新建项目,在扩展性标签中,选择Editor Classifier模版,命名为ShaderEditor,点击确定。
Visual Studio为我们生成了如下几个文件。
ShaderEditorFormat.cs文件的默认代码如下:
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "ShaderEditor")]
[Name("ShaderEditor")]
[UserVisible(true)] //this should be visible to the end user
[Order(Before = Priority.Default)] //set the priority to be after the default classifiers
internal sealed class ShaderEditorFormat : ClassificationFormatDefinition {
/// <summary>
/// Defines the visual format for the "ShaderEditor" classification type
/// </summary>
public ShaderEditorFormat() {
this.DisplayName = "ShaderEditor"; //human readable version of the name
this.BackgroundColor = Colors.BlueViolet;
this.TextDecorations = System.Windows.TextDecorations.Underline;
}
}
这段代码定义了一个名为"ShaderEditor"的着色类型,编译工程并运行,我们可以在Visual Studio实验实例的工具->选项->字体和颜色中找到一个名为"ShaderEditor"的条目。同时我们会发现所有文本文件的颜色都变成了Colors.BlueViolet并带上了下划线。修改this.DisplayName = "ShaderEditor"的内容,可以改变在字体和颜色中显示的名字。下面的格式设置可以任意修改成喜欢的样式,但要注意在这里的格式只是插件首次安装时的默认设置,这些条目和其它着色选项一样,都可以被用户任意更改。
3. 创建GLSL的着色类型
我们已经了解了如何将着色类型添加到Visual Studio,现在修改ShaderEditorFormat.cs,添加我们的着色类型。
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "GLSLText")]
[Name("GLSLText")]
[UserVisible(true)]
[Order(Before = Priority.Default)]
internal sealed class GLSLTextFormatDefinition : ClassificationFormatDefinition {
public GLSLTextFormatDefinition() {
this.DisplayName = "GLSL文本";
this.ForegroundColor = Colors.Brown;
}
} [Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "GLSLIdentifier")]
[Name("GLSLIdentifier")]
[UserVisible(true)]
[Order(Before = Priority.Default)]
internal sealed class GLSLIdentifierFormatDefinition : ClassificationFormatDefinition {
public GLSLIdentifierFormatDefinition() {
this.DisplayName = "GLSL标识符";
this.ForegroundColor = Colors.Brown;
}
} [Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "GLSLComment")]
[Name("GLSLComment")]
[UserVisible(true)]
[Order(Before = Priority.Default)]
internal sealed class GLSLCommentFormatDefinition : ClassificationFormatDefinition {
public GLSLCommentFormatDefinition() {
this.DisplayName = "GLSL注释";
this.ForegroundColor = Colors.DarkGray;
}
} [Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "GLSLKeyword")]
[Name("GLSLKeyword")]
[UserVisible(true)]
[Order(Before = Priority.Default)]
internal sealed class GLSLKeywordFormatDefinition : ClassificationFormatDefinition {
public GLSLKeywordFormatDefinition() {
this.DisplayName = "GLSL关键字";
this.ForegroundColor = Colors.Blue;
}
} [Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "GLSLClass")]
[Name("GLSLClass")]
[UserVisible(true)]
[Order(Before = Priority.Default)]
internal sealed class GLSLClassFormatDefinition : ClassificationFormatDefinition {
public GLSLClassFormatDefinition() {
this.DisplayName = "GLSL类型";
this.ForegroundColor = Colors.Green;
}
} [Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "GLSLQualifier")]
[Name("GLSLQualifier")]
[UserVisible(true)]
[Order(Before = Priority.Default)]
internal sealed class GLSLQualifierFormatDefinition : ClassificationFormatDefinition {
public GLSLQualifierFormatDefinition() {
this.DisplayName = "GLSL限定符";
this.ForegroundColor = Colors.Pink;
}
} [Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "GLSLVariable")]
[Name("GLSLVariable")]
[UserVisible(true)]
[Order(Before = Priority.Default)]
internal sealed class GLSLVariableFormatDefinition : ClassificationFormatDefinition {
public GLSLVariableFormatDefinition() {
this.DisplayName = "GLSL系统变量";
this.ForegroundColor = Colors.DarkOrange;
}
} [Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = "GLSLFunction")]
[Name("GLSLFunction")]
[UserVisible(true)]
[Order(Before = Priority.Default)]
internal sealed class GLSLFunctionFormatDefinition : ClassificationFormatDefinition {
public GLSLFunctionFormatDefinition() {
this.DisplayName = "GLSL系统函数";
this.ForegroundColor = Colors.DarkTurquoise;
}
}
4. 导出着色类型
Editor Classifier使用了MEF框架,关于MEF的具体细节,请参考MSDN的相关文档。
我们需要注意的是,在MEF中,光定义了着色类型还不够,我们需要导出一个ClassificationTypeDefinition,才能在系统中生效。
打开ShaderEditorType.cs,我们看到系统生成的代码如下:
internal static class ShaderEditorClassificationDefinition {
[Export(typeof(ClassificationTypeDefinition))]
[Name("ShaderEditor")]
internal static ClassificationTypeDefinition ShaderEditorType = null;
}
这里的Name与之前默认生成的ShaderEditor相同,同理,我们将这里的代码修改成方才定义的类型
internal static class ShaderEditorClassificationDefinition {
[Export(typeof(ClassificationTypeDefinition))]
[Name("GLSLText")]
internal static ClassificationTypeDefinition GLSLTextType = null; [Export(typeof(ClassificationTypeDefinition))]
[Name("GLSLIdentifier")]
internal static ClassificationTypeDefinition GLSLIdentifierType = null; [Export(typeof(ClassificationTypeDefinition))]
[Name("GLSLComment")]
internal static ClassificationTypeDefinition GLSLCommentType = null; [Export(typeof(ClassificationTypeDefinition))]
[Name("GLSLKeyword")]
internal static ClassificationTypeDefinition GLSLKeywordType = null; [Export(typeof(ClassificationTypeDefinition))]
[Name("GLSLClass")]
internal static ClassificationTypeDefinition GLSLClassType = null; [Export(typeof(ClassificationTypeDefinition))]
[Name("GLSLQualifier")]
internal static ClassificationTypeDefinition GLSLQualifierType = null; [Export(typeof(ClassificationTypeDefinition))]
[Name("GLSLVariable")]
internal static ClassificationTypeDefinition GLSLVariableType = null; [Export(typeof(ClassificationTypeDefinition))]
[Name("GLSLFunction")]
internal static ClassificationTypeDefinition GLSLFunctionType = null;
}
5. 关联文件类型
打开ShaderEditor.cs
[Export(typeof(IClassifierProvider))]
[ContentType("text")]
internal class ShaderEditorProvider : IClassifierProvider {
[Import]
internal IClassificationTypeRegistryService ClassificationRegistry = null; // Set via MEF public IClassifier GetClassifier(ITextBuffer buffer) {
return buffer.Properties.GetOrCreateSingletonProperty<ShaderEditor>(delegate { return new ShaderEditor(ClassificationRegistry); });
}
}
代码ContentType("text")创建了一个Provider并将它们对所有text类型的文件生效。
GLSL主要的文件扩展名为.vsh和.fsh,为了只对这两个扩展名生效,我们需要自定义一个ContentType,并创建两个扩展名关联。将上述代码修改为:
[Export(typeof(ITaggerProvider))]
[ContentType("glsl")]
[TagType(typeof(ClassificationTag))]
internal sealed class GLSLClassifierProvider : ITaggerProvider { [Export]
[Name("glsl")]
[BaseDefinition("code")]
internal static ContentTypeDefinition GLSLContentType = null; [Export]
[FileExtension(".vsh")]
[ContentType("glsl")]
internal static FileExtensionToContentTypeDefinition GLSLVshType = null; [Export]
[FileExtension(".fsh")]
[ContentType("glsl")]
internal static FileExtensionToContentTypeDefinition GLSLFshType = null; [Import]
internal IClassificationTypeRegistryService classificationTypeRegistry = null; [Import]
internal IBufferTagAggregatorFactoryService aggregatorFactory = null; public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
return new GLSLClassifier(buffer, classificationTypeRegistry) as ITagger<T>;
}
}
这样我们就创建了只针对vsh和fsh文件生效的Editor。
6. 使用gplex进行词法分析
我们需要使用词法分析扫描器来实现具体的着色功能,gplex可以为我们生成C#语言的扫描器,下载地址:
解压后在binaries文件夹下找到gplex.exe,把它拷贝到项目的根目录下。
在项目根目录下新建一个GLSL文件夹,新建GLSLLexer.lex文件。并把它们添加到proj中。
在proj上右键->属性,在生成事件选项卡中,在预先生成事件命令行中输入
cd $(ProjectDir)GLSL\
$(ProjectDir)\gplex GLSLLexer
打开GLSLLexer.lex,写入以下代码:
%option unicode, codepage:raw %{
// User code is all now in ScanHelper.cs
%} %namespace Shane
%option verbose, summary, noparser, nofiles, unicode %{
public int nextToken() { return yylex(); }
public int getPos() { return yypos; }
public int getLength() { return yyleng; }
%} //=============================================================
//============================================================= number ([-])+
chars [A-Za-z]
cstring [A-Za-z_]
blank " "
delim [ \t\n]
word {chars}+
singleLineComment "//"[^\n]*
multiLineComment "/*"[^*]*\*(\*|([^*/]([^*])*\*))*\/ comment {multiLineComment}|{singleLineComment}
class bool|int|float|bvec|ivec|vec|vec2|vec3|vec4|mat2|mat3|mat4|sampler1D|sampler2D|sampler3D|samplerCube|sampler1DShadow|sampler2DShadow
keyword return|if|else|while|do|for|foreach|break|continue|switch|case|default|goto|class|struct|enum|extern|interface|namespace|public|static|this|volatile|using|in|out|true|false
qualifier const|attribute|uniform|varying
systemVariable gl_BackColor|gl_BackLightModelProduct|gl_BackLightProduct|gl_BackMaterial|gl_BackSecondaryColor|gl_ClipPlane|gl_ClipVertex|gl_Color|gl_DepthRange|gl_DepthRangeParameters|gl_EyePlaneQ|gl_EyePlaneR|gl_EyePlaneS|gl_EyePlaneT|gl_Fog|gl_FogCoord|gl_FogFragCoord|gl_FogParameters|gl_FragColor|gl_FragCoord|gl_FragData|gl_FragDepth|gl_FrontColor|gl_FrontFacing|gl_FrontLightModelProduct|gl_FrontLightProduct|gl_FrontMaterial|gl_FrontSecondaryColor|gl_LightModel|gl_LightModelParameters|gl_LightModelProducts|gl_LightProducts|gl_LightSource|gl_LightSourceParameters|gl_MaterialParameters|gl_MaxClipPlanes|gl_MaxCombinedTextureImageUnits|gl_MaxDrawBuffers|gl_MaxFragmentUniformComponents|gl_MaxLights|gl_MaxTextureCoords|gl_MaxTextureImageUnits|gl_MaxTextureUnits|gl_MaxVaryingFloats|gl_MaxVertexAttribs|gl_MaxVertexTextureImageUnits|gl_MaxVertexUniformComponents|gl_ModelViewMatrix|gl_ModelViewMatrixInverse|gl_ModelViewMatrixInverseTranspose|gl_ModelViewMatrixTranspose|gl_ModelViewProjectionMatrix|gl_ModelViewProjectionMatrixInverse|gl_ModelViewProjectionMatrixInverseTranspose|gl_ModelViewProjectionMatrixTranspose|gl_MultiTexCoord0|gl_MultiTexCoord1|gl_MultiTexCoord10|gl_MultiTexCoord11|gl_MultiTexCoord2|gl_MultiTexCoord3|gl_MultiTexCoord4|gl_MultiTexCoord5|gl_MultiTexCoord6|gl_MultiTexCoord7|gl_MultiTexCoord8|gl_MultiTexCoord9|gl_Normal|gl_NormalMatrix|gl_NormalScale|gl_ObjectPlaneQ|gl_ObjectPlaneR|gl_ObjectPlaneS|gl_ObjectPlaneT|gl_Point|gl_PointParameters|gl_PointSize|gl_Position|gl_ProjectionMatrix|gl_ProjectionMatrixInverse|gl_ProjectionMatrixInverseTranspose|gl_ProjectionMatrixTranspose|gl_SecondaryColor|gl_TexCoord|gl_TextureEnvColor|gl_TextureMatrix|gl_TextureMatrixInverse|gl_TextureMatrixInverseTranspose|gl_TextureMatrixTranspose|gl_Vertex
systemFunction radians|degress|sin|cos|tan|asin|acos|atan|pow|exp|log|exp2|log2|sqrt|inversesqrt|abs|sign|floor|ceil|fract|mod|min|max|clamp|mix|step|smoothstep|length|distance|dot|cross|normalize|faceforward|reflect|matrixCompMult|lessThan|lessThanEqual|greaterThan|greaterThanEqual|equal|notEqual|any|all|not|texture2D|texture2DProj|texture2DLod|texture2DProjLod|textureCube|textureCubeLod
identifier {cstring}+{number}*[{cstring}@]*{number}* %% {keyword} return (int)GLSLTokenType.Keyword;
{class} return (int)GLSLTokenType.Class;
{qualifier} return (int)GLSLTokenType.Qualifier;
{systemVariable} return (int)GLSLTokenType.SystemVariable;
{systemFunction} return (int)GLSLTokenType.SystemFunction;
{identifier} return (int)GLSLTokenType.Identifier;
{comment} return (int)GLSLTokenType.Comment; %%
保存并关闭,这时生成一下项目,我们会看到在GLSL目录下生成了GLSLLexer.cs文件,同样把这个文件添加到proj中。
7. 处理扫描结果
接下来我们要在ShaderEditor.cs中处理我们的扫描结果,并最终对匹配的代码行进行着色。
首先删除默认创建的ShaderEditor类。
添加一个GLSLToken枚举,这个枚举就是GLSLLexer.cs返回的枚举类型,它用来通知我们当前的语句块是哪个类型。
代码如下:
public enum GLSLTokenType {
Text = ,
Keyword,
Comment,
Identifier,
Class,
Qualifier,
SystemVariable,
SystemFunction
}
创建我们自己的ShaderEditor类,代码如下:
internal sealed class GLSLClassifier : ITagger<ClassificationTag> {
internal GLSLClassifier(ITextBuffer buffer, IClassificationTypeRegistryService typeService) {
textBuffer = buffer;
typeDic = new Dictionary<GLSLTokenType, IClassificationType>();
typeDic[GLSLTokenType.Text] = typeService.GetClassificationType("GLSLText");
typeDic[GLSLTokenType.Identifier] = typeService.GetClassificationType("GLSLIdentifier");
typeDic[GLSLTokenType.Keyword] = typeService.GetClassificationType("GLSLKeyword");
typeDic[GLSLTokenType.Class] = typeService.GetClassificationType("GLSLClass");
typeDic[GLSLTokenType.Comment] = typeService.GetClassificationType("GLSLComment");
typeDic[GLSLTokenType.Qualifier] = typeService.GetClassificationType("GLSLQualifier");
typeDic[GLSLTokenType.SystemVariable] = typeService.GetClassificationType("GLSLVariable");
typeDic[GLSLTokenType.SystemFunction] = typeService.GetClassificationType("GLSLFunction");
} public event EventHandler<SnapshotSpanEventArgs> TagsChanged {
add { }
remove { }
} public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans) {
IClassificationType classification = typeDic[GLSLTokenType.Text];
string text = spans[].Snapshot.GetText();
yield return new TagSpan<ClassificationTag>(
new SnapshotSpan(spans[].Snapshot, new Span(, text.Length)),
new ClassificationTag(classification));
scanner.SetSource(text, );
int tok;
do {
tok = scanner.nextToken();
int pos = scanner.getPos();
int len = scanner.getLength();
int total = text.Length;
if (pos < || len < || pos > total) {
continue;
}
if (pos + len > total) {
len = total - pos;
}
if (typeDic.TryGetValue((GLSLTokenType)tok, out classification)) {
yield return new TagSpan<ClassificationTag>(
new SnapshotSpan(spans[].Snapshot, new Span(pos, len)),
new ClassificationTag(classification));
}
} while (tok > (int)Tokens.EOF);
} ITextBuffer textBuffer;
IDictionary<GLSLTokenType, IClassificationType> typeDic;
Scanner scanner = new Scanner();
}
TagsChanged事件保证在代码发生改变时实时刷新编辑器。
构造方法中,通过typeService.GetClassificationType("GLSLIdentifier")取得我们定义的实例,并把它们和枚举关联起来,
GetClassificationType的参数传入着色类型的Name。
GetTags方法是由系统调用的迭代方法,yield return new TagSpan<ClassificationTag>()返回我们的着色对象,即可实现着色效果。
编译并运行,可以看到vsh和fsh已经有了语法着色了。
本文由哈萨雅琪原创,转载请注明出处。
使用Visual Studio SDK制作GLSL词法着色插件的更多相关文章
- Visual Studio Package 插件开发之自动生成实体工具(Visual Studio SDK)
前言 这一篇是VS插件基于Visual Studio SDK扩展开发的,可能有些朋友看到[生成实体]心里可能会暗想,T4模板都可以做了.动软不是已经做了么.不就是读库保存文件到指定路径么…… 我希望做 ...
- Visual Studio Package 插件开发(Visual Studio SDK)
背景 这段时间公司新做了一个支付系统,里面有N个后台服务,每次有更新修改,拷贝打包发布包“不亦乐乎”...于是我想要不要自己定制个打包插件. 部分朋友可能会认为,有现成的可以去找一个,干嘛不用持续集成 ...
- visual studio code开发代码片段扩展插件
背景 visual studio code编辑器强大在于可以自己扩展插件,不仅可以去插件市场下载,也可以按照官方的API很方便的制作适合自己的插件: 自己最近在开发一个手机端网站项目,基于vant项目 ...
- 艾伦 Visual Studio 批量自动化代码操作工具-VS插件发布
艾伦 Visual Studio 批量自动化代码操作工具 以下简称--艾伦工具箱. 艾伦工具箱是一个多文件批量处理插件,目的是为了广大开发者提高开发效率,减少项目代码规范化审计,缩短开发者的项目开发周 ...
- 如何在Visual Studio中开发自己的代码生成器插件
Visual Studio是美国微软公司开发的一个基本完整的开发工具集,它包括了整个软件生命周期中所需要的大部分工具,如UML工具.代码管控工具.集成开发环境(IDE)等等,且所写的目标代码适用于微 ...
- 提高Visual Studio开发性能的几款插件
通过打开Visual Studio,单机TOOLS—Extensions and Updates-Online-Visual Studio Gallery(工具-扩展和更新-联网-Visual Stu ...
- Visual Studio 2012 比较好用的插件推荐
为了高效率的开发,下面笔者推荐几款非常不错的插件,方便大家. 以上控件的安装方式是: 然后通过联网的方式下载,安装后,需要重启一下Visual Studio方可使用.
- Visual Studio的2个有趣的插件:声音控制和放屁:)
.NET Slave | Talk to, hear, touch and see your code介绍了2个有趣的Visual Studio的插件,你可以通过它们和你的代码对话. 声音控制(Voi ...
- Visual Studio 2008中FormatX源代码格式化插件
原地址:http://www.cr173.com/html/15492_1.html 我总是对组里的兄弟代码规范性近乎完美的要求,举个简单的例子: 1. 每个方法必须有注释,方法参数详细说明 2. ...
随机推荐
- Python标准模块--ContextManager
1 模块简介 在数年前,Python 2.5 加入了一个非常特殊的关键字,就是with.with语句允许开发者创建上下文管理器.什么是上下文管理器?上下文管理器就是允许你可以自动地开始和结束一些事情. ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(73)-微信公众平台开发-消息管理
系列目录 前言 回顾上一节,我们熟悉的了解了消息的请求和响应,这一节我们来建立数据库的表,表的设计蛮复杂 你也可以按自己所分析的情形结构来建表 必须非常熟悉表的结果才能运用这张表,这表表的情形涵盖比较 ...
- ASP.NET Core 中文文档 第四章 MVC(4.2)控制器操作的路由
原文:Routing to Controller Actions 作者:Ryan Nowak.Rick Anderson 翻译:娄宇(Lyrics) 校对:何镇汐.姚阿勇(Dr.Yao) ASP.NE ...
- C# 对象实例化 用json保存 泛型类 可以很方便的保存程序设置
参考页面: http://www.yuanjiaocheng.net/webapi/test-webapi.html http://www.yuanjiaocheng.net/webapi/web-a ...
- 高德地图api实现地址和经纬度的转换(python)
利用高德地图web服务api实现地理/逆地址编码 api使用具体方法请查看官方文档 文档网址:http://lbs.amap.com/api/webservice/guide/api/georegeo ...
- jquery.each()
$(selector).each(function(index,element)) index - 选择器的 index 位置 element - 当前的元素(也可使用 "this" ...
- RMS去除在线认证
在微软 OS 平台创建打开 RMS 文档如何避免时延 相信我们在企业内部的环境中已经部署了微软最新的OS平台,Windows 7和Windows 2008 R2,在这些OS平台上使用IRM功能时,您有 ...
- windows 7(32/64位)GHO安装指南(系统安装篇)~重点哦!!~~~~
经过了前三篇的铺垫,我们终于来到了最重要的部分~~如果没看过前几篇的小伙伴们,可以出门右转~~用十几分钟回顾一下~~然后在看这篇会感觉不一样的~~~~ 下面让我们来正式开始吧 我们进入大白菜的桌面是酱 ...
- Struts框架的核心业务
Struts的核心业务 Struts核心业务有很多,这里主要介绍了比较简单一些的: 请求数据的处理,和数据自动封装,类型自动转换 1.Struts中数据处理 1.1.方式1:直接过去servletap ...
- 学习笔记:Maven构造版本号的方法解决浏览器缓存问题
需要解决的问题 在做WEB系统开发时,为了提高性能会利用浏览器的缓存功能,其实即使不显式的申明缓存,现代的浏览器都会对静态文件(js.css.图片之类)缓存.但也正因为这个问题导致一个问题,就是资源的 ...