├── .gitignore
├── README.md
├── resources
├── META-INF
│ ├── plugin.xml
│ └── pluginIcon.svg
├── icons
│ ├── weapp.png
│ ├── wxml.png
│ └── wxss.png
└── lib
│ └── weixin-app
│ ├── index.d.ts
│ ├── tsconfig.json
│ ├── tslint.json
│ └── weixin-app-tests.ts
├── src
└── com
│ └── ytw88
│ └── weappsupport
│ ├── IconsLoader.java
│ ├── WeappFramework.java
│ ├── WeappJSPredefinedLibraryProvider.java
│ ├── WeappModuleBuilder.java
│ ├── WeappModuleType.java
│ ├── WeappModuleWizardStep.java
│ ├── WeappProjectStructureDetector.java
│ ├── WeappStartupActivity.java
│ ├── wxml
│ ├── WxmlColorSettingsPage.java
│ ├── WxmlFileType.java
│ ├── WxmlFileTypeFactory.java
│ ├── WxmlLanguage.java
│ ├── WxmlLexerAdapter.java
│ ├── WxmlParserDefinition.java
│ ├── WxmlSyntaxHighlighter.java
│ ├── WxmlSyntaxHighlighterFactory.java
│ ├── grammer
│ │ ├── WxmlLexer.flex
│ │ ├── tutorial.bnf
│ │ ├── tutorial2.bnf
│ │ └── wxml.bnf
│ ├── parser
│ │ └── WxmlParserUtil.java
│ └── psi
│ │ ├── WxmlElementType.java
│ │ ├── WxmlFile.java
│ │ └── WxmlTokenType.java
│ └── wxss
│ ├── WxssElementDescriptorProvider.java
│ ├── WxssElementType.java
│ ├── WxssElementTypes.java
│ ├── WxssFileElementType.java
│ ├── WxssFileType.java
│ ├── WxssFileTypeFactory.java
│ ├── WxssLanguage.java
│ ├── parser
│ ├── WxssParser.java
│ └── WxssParserDefinition.java
│ └── psi
│ ├── WxssFile.java
│ └── impl
│ └── WxssFileImpl.java
└── weapp-support-plugin.iml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | gen/
3 | weapp-support-plugin.jar
4 | idea-flex.skeleton
5 | jflex-1.7.0-2.jar
6 | out/
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WeApp Support Plugin 微信小程序支持插件
2 |
3 | 想要实现一个微信小程序的JetBrain IDE插件,但是经过尝试发现写BNF的Grammar对我来说实在有点困
4 | 难了,短时间之内没有可能全面的学习这个东东,近期工作上的事情也还比较忙,只好将这个项目暂时搁置了。
5 |
6 | JetBrains的Plugin开发虽然官方有很多文档,但是工程太庞大了,而且这几天学习研究过程中似乎搜索的
7 | 结论都提到无法对现有的语言支持做扩展。其实微信小程序的支持,只需要对xml(wxml),css(wxss),
8 | js做一些扩展就可以用了,可惜的是没有发现支持的扩展路径。这两天尝试写wxml的bnf语法文件,已经有点
9 | 焦头烂额的感觉。
10 |
11 | IDEA官方文档中,对于语言支持插件开发的文档"Part VII - Custom Languages"包括两大部分:
12 |
13 | [介绍框架](http://www.jetbrains.org/intellij/sdk/docs/reference_guide/custom_language_support.html)
14 |
15 | [具体的教程](http://www.jetbrains.org/intellij/sdk/docs/tutorials/custom_language_support_tutorial.html)
16 |
17 | ## TODO list
18 | 近期希望完成以下功能列表,如果有熟悉idea插件开发的大牛能够协助一二自然是最好不过的了。实在是难以找到有效的文档来发现最佳的实现方案。
19 |
20 | * [ ] css中增加对rpx单位的支持
21 | * [ ] wxml中增加对wx:if/wx:else/wx:elif的支持
22 | * [ ] 增加对微信小程序工程检查的支持
23 |
24 | ## 更新日记
25 |
26 | ### 2018.12.18
27 | 今天发现jetbrains还有一个社区的js库定义,专门辅助完善一些js框架的类库定义。可以在`Preferences |
28 | Languages & Frameworks | JavaScript | Libraries`设置中添加一些library,而且这个设置界面
29 | 直接提供了下载功能,列举了git上该开源项目相关的定义,其中找到三个可能和微信小程序有关的
30 | * weapp-api
31 | * wegame-api
32 | * wexin-app
33 |
34 | git上的开源项目地址: [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped)
35 |
36 | 关于ide的javascript library介绍:
37 |
38 | [How WebStorm Works: Completion for JavaScript Libraries](https://blog.jetbrains.com/webstorm/2014/07/how-webstorm-works-completion-for-javascript-libraries/)
39 |
40 | [Configuring JavaScript Libraries](https://www.jetbrains.com/help/idea/configuring-javascript-libraries.html)
41 |
42 | 文2中注明了
43 | > This feature is only supported in the Ultimate edition.
44 |
45 | 目前在webstorm中测试过,三个library都可以用,但是weapp-api定义没有wexin-app定义全面
46 |
47 | ### 2018.12.19
48 | 终于找到了一个合适的方案来把wexin-app的api添加到plugin,直接添加到javascript的支持
49 | 首先要从IDEA的plugin目录中找到JavascriptLanguage,从其子目录lib中找到
50 | JavaScriptLanguage.jar,最好是将这个jar包添加到sdk的library中去,这样才能够在开发插件
51 | 的时候使用
52 |
53 | 在`plugin.xml`中加入
54 | ```
55 | JavaScript
56 | ...
57 |
58 |
59 |
60 |
61 | ```
62 | * depends不是必须的,这个会使得插件只能在支持Javascript的平台上使用
63 | * extensions的声明,是为javascript的`extensionPoint:predefinedLibraryProvider`添加实现
64 |
65 | #### 弹窗显示内容
66 | 跟怒官方文档的提示,可以在插件代码中使用来弹出对话框,显示一些信息,另外用logger来输出的log暂时没找到显示窗口
67 | > Messages.showInfoMessage("Message", "Title");
68 |
69 | #### 将文件注册为已知类型
70 | 如果是在FileTypeFactory的createFileTypes中使用FileTypeManager的getFileTypeByExtension会导致循环依
71 | 赖的异常,从而使得ide无法启动,需要在/User/Library/Application Support/xxxxx/中找到plugin并删除方可正
72 | 常启动ide。
73 |
74 | 目前将Wxml文件注册为Html文件,做法是在WxmlFileTypeFactory中直接使用HtmlFileType返回(因为HtmlFileType
75 | 的构造函数都不是public/protected的,故而无法继承),而wxss文件则通过将WxssFileType继承CssFileType来解决
76 |
77 | 以后可以抽时间继续完善wxml的更多的语法支持,比如
78 | > * *
2 | com.ytw88.weappsupport
3 | WeChat weapp Support
4 | 0.1.0
5 | kerlw
6 |
7 |
9 |
10 | - Using predefined js library base on https://github.com/DefinitelyTyped/DefinitelyTyped
11 | - Use HTMLFileType for wxml file type
12 | - Use CSSFileType for wxss file type
13 | - We-game support not included yet
14 | - We-app framework not support yet
15 |
16 |
17 | ]]>
18 |
19 | 0.1.0
21 | Just a simple ide for we-app
22 | ]]>
23 |
24 |
25 |
26 |
27 |
28 |
30 |
31 | com.intellij.modules.lang
32 |
33 | JavaScript
34 | com.intellij.css
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/resources/META-INF/pluginIcon.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/resources/icons/weapp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerlw/weapp-support-plugin/f6f5ad9627ce407823ae05caaa4021b5aa000834/resources/icons/weapp.png
--------------------------------------------------------------------------------
/resources/icons/wxml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerlw/weapp-support-plugin/f6f5ad9627ce407823ae05caaa4021b5aa000834/resources/icons/wxml.png
--------------------------------------------------------------------------------
/resources/icons/wxss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kerlw/weapp-support-plugin/f6f5ad9627ce407823ae05caaa4021b5aa000834/resources/icons/wxss.png
--------------------------------------------------------------------------------
/resources/lib/weixin-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "lib": ["es6", "dom"],
5 | "noImplicitAny": true,
6 | "noImplicitThis": true,
7 | "strictNullChecks": true,
8 | "strictFunctionTypes": true,
9 | "baseUrl": "../",
10 | "typeRoots": ["../"],
11 | "types": [],
12 | "noEmit": true,
13 | "forceConsistentCasingInFileNames": true,
14 | "esModuleInterop": true
15 | },
16 | "files": ["index.d.ts", "weixin-app-tests.ts"],
17 | "exclude": [".prettierrc"]
18 | }
19 |
--------------------------------------------------------------------------------
/resources/lib/weixin-app/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "dtslint/dt.json",
3 | "rules": {
4 | "no-any-union": false,
5 | "no-unnecessary-generics": false
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/resources/lib/weixin-app/weixin-app-tests.ts:
--------------------------------------------------------------------------------
1 | getCurrentPages();
2 |
3 | interface MyOwnEvent
4 | extends wx.CustomEvent<
5 | "my-own",
6 | {
7 | hello: string;
8 | }
9 | > {}
10 |
11 | let behavior = Behavior({
12 | behaviors: [],
13 | properties: {
14 | myBehaviorProperty: {
15 | type: String
16 | }
17 | },
18 | data: {
19 | myBehaviorData: ""
20 | },
21 | attached() {},
22 | methods: {
23 | myBehaviorMethod() {
24 | const s: string = this.data.myBehaviorData;
25 | }
26 | }
27 | });
28 |
29 | Component({
30 | behaviors: [behavior, "wx://form-field"],
31 |
32 | options: {
33 | multipleSlots: false,
34 | addGlobalClass: false
35 | },
36 |
37 | properties: {
38 | myProperty: {
39 | // 属性名
40 | type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型)
41 | value: "", // 属性初始值(可选),如果未指定则会根据类型选择一个
42 | observer(newVal: string, oldVal: string, changedPath: string) {
43 | const anotherKey = newVal + changedPath;
44 | this.setData({
45 | anotherKey
46 | });
47 | } // 属性被改变时执行的函数(可选),也可以写成在methods段中定义的方法名字符串, 如:'_propertyChange'
48 | },
49 | myProperty2: String // 简化的定义方式
50 | },
51 | data: {
52 | key: "value",
53 | anotherKey: "value"
54 | }, // 私有数据,可用于模版渲染
55 |
56 | lifetimes: {
57 | attached() {
58 | wx.setEnableDebug({
59 | enableDebug: true,
60 | success(res) {}
61 | });
62 |
63 | wx.reportMonitor("123", 123);
64 |
65 | wx.getLogManager().info("123");
66 | wx.getLogManager().log("123");
67 | wx.getLogManager().warn("123");
68 | wx.getLogManager().debug("123");
69 |
70 | this.createIntersectionObserver({}).observe("123", res => {
71 | res.id;
72 | });
73 | },
74 |
75 | detached() {
76 | this.setData(
77 | {
78 | key: null
79 | },
80 | () => {}
81 | );
82 | }
83 | },
84 |
85 | pageLifetimes: {
86 | show() {},
87 | hide() {}
88 | },
89 |
90 | // 生命周期函数,可以为函数,或一个在methods段中定义的方法名
91 | attached() {
92 | this.setData(
93 | {
94 | key: "123"
95 | },
96 | () => {}
97 | );
98 | },
99 |
100 | moved() {},
101 |
102 | detached() {},
103 |
104 | methods: {
105 | readMyDataAndMyProps() {
106 | const stringValue1: string = this.data.myProperty;
107 | const stringValue2: string = this.data.myProperty2;
108 | const stringValue3: string = this.data.key;
109 | this.data.anotherKey;
110 | this.properties.myProperty;
111 | this.properties.myProperty2;
112 | this.properties.key;
113 | this.properties.anotherKey;
114 | this.setData({
115 | key: stringValue1 + stringValue2 + stringValue3
116 | });
117 | },
118 | onMyButtonTap() {
119 | // 更新属性和数据的方法与更新页面数据的方法类似
120 | this.setData({
121 | key: 123 // note this is edge case where it cannot detect wrong types...
122 | });
123 | },
124 | _myPrivateMethod() {
125 | // 内部方法建议以下划线开头
126 | // this.replaceDataOnPath(['A', 0, 'B'], 'myPrivateData'); // 这里将 data.A[0].B 设为 'myPrivateData'
127 | // this.applyDataUpdates();
128 | this.setData({
129 | anotherKey: 123
130 | });
131 | },
132 | _propertyChange(newVal: string, oldVal: string) {
133 | //
134 | }
135 | },
136 | relations: {
137 | "./custom-ul": {
138 | type: "parent", // 关联的目标节点应为父节点
139 | linked(target: wx.Component<{ key: string }, {}>) {
140 | // 每次被插入到custom-ul时执行,target是custom-ul节点实例对象,触发在attached生命周期之后
141 | target.data.key;
142 | },
143 | linkChanged(target: wx.Component<{ key: string }, {}>) {
144 | // 每次被移动后执行,target是custom-ul节点实例对象,触发在moved生命周期之后
145 | target.data.key;
146 | },
147 | unlinked(target: wx.Component<{ key: string }, {}>) {
148 | // 每次被移除时执行,target是custom-ul节点实例对象,触发在detached生命周期之后
149 | target.data.key;
150 | }
151 | }
152 | }
153 | });
154 |
155 | // index.js
156 | Page({
157 | data: {
158 | text: "This is page data."
159 | },
160 | onLoad() {
161 | // Do some initialize when page load.
162 | this.setData({}, () => {
163 | // callback
164 | });
165 | },
166 | onReady: () => {
167 | // Do something when page ready.
168 | },
169 | onShow: () => {
170 | // Do something when page show.
171 | },
172 | onHide: () => {
173 | // Do something when page hide.
174 | },
175 | onUnload: () => {
176 | // Do something when page close.
177 | },
178 | onPullDownRefresh: () => {
179 | // Do something when pull down.
180 | },
181 | onReachBottom: () => {
182 | // Do something when page reach bottom.
183 | },
184 | onShareAppMessage: res => {
185 | if (res && res.from === "menu") {
186 | //
187 | }
188 | // return custom share data when user share.
189 | return {
190 | success(res) {
191 | console.log(res.shareTickets.length);
192 | }
193 | };
194 | },
195 | onPageScroll() {
196 | wx.createIntersectionObserver(this, {})
197 | .relativeToViewport()
198 | .observe("div", res => {
199 | console.log(res.id);
200 | console.log(res.dataset);
201 | console.log(res.intersectionRatio);
202 | console.log(res.intersectionRect.left);
203 | console.log(res.intersectionRect.top);
204 | console.log(res.intersectionRect.width);
205 | console.log(res.intersectionRect.height);
206 | })
207 | .disconnect();
208 | },
209 | onTabItemTap(item: any) {
210 | this.setData({
211 | 1: null,
212 | _2: "undefined"
213 | });
214 | console.log(item.index);
215 | console.log(item.pagePath);
216 | console.log(item.text);
217 | },
218 | // Event handler.
219 | viewTap() {
220 | this.setData(
221 | {
222 | text: "Set some data for updating view."
223 | },
224 | () => {
225 | // this is setData callback
226 | }
227 | );
228 | },
229 | customData: {
230 | hi: "MINA"
231 | },
232 | onMyOwnEvent(e: MyOwnEvent) {
233 | e.detail.hello;
234 | },
235 | onTouchStart(e: wx.TouchStartEvent) {
236 | e.touches;
237 | e.detail.x;
238 | e.detail.y;
239 | },
240 | onTouchEnd(e: wx.TouchEndEvent) {
241 | e.touches;
242 | e.detail.x;
243 | e.detail.y;
244 | },
245 | onTouchCancel(e: wx.TouchCancelEvent) {
246 | e.touches;
247 | e.detail.x;
248 | e.detail.y;
249 | },
250 | onTouchMove(e: wx.TouchMoveEvent) {
251 | e.touches;
252 | e.detail.x;
253 | e.detail.y;
254 | }
255 | });
256 |
257 | Page({
258 | getScrollOffset: () => {
259 | wx.createSelectorQuery()
260 | .selectViewport()
261 | .scrollOffset(res => {
262 | res.id; // 节点的ID
263 | res.dataset; // 节点的dataset
264 | res.scrollLeft; // 节点的水平滚动位置
265 | res.scrollTop; // 节点的竖直滚动位置
266 | })
267 | .exec();
268 | }
269 | });
270 |
271 | Page({
272 | getFields: () => {
273 | wx.createSelectorQuery()
274 | .select("#the-id")
275 | .fields(
276 | {
277 | id: true,
278 | dataset: true,
279 | size: true,
280 | scrollOffset: true,
281 | properties: ["scrollX", "scrollY"]
282 | },
283 | res => {
284 | // res.
285 | res.dataset; // 节点的dataset
286 | res.width; // 节点的宽度
287 | res.height; // 节点的高度
288 | res.scrollLeft; // 节点的水平滚动位置
289 | res.scrollTop; // 节点的竖直滚动位置
290 | res.scrollX; // 节点 scroll-x 属性的当前值
291 | res.scrollY; // 节点 scroll-x 属性的当前值
292 | }
293 | )
294 | .exec();
295 | }
296 | });
297 |
298 | Page({
299 | getRect: () => {
300 | wx.createSelectorQuery()
301 | .select("#the-id")
302 | .boundingClientRect((rect: wx.NodesRefRect) => {
303 | rect.id; // 节点的ID
304 | rect.dataset; // 节点的dataset
305 | rect.left; // 节点的左边界坐标
306 | rect.right; // 节点的右边界坐标
307 | rect.top; // 节点的上边界坐标
308 | rect.bottom; // 节点的下边界坐标
309 | rect.width; // 节点的宽度
310 | rect.height; // 节点的高度
311 | })
312 | .exec();
313 | },
314 | getAllRects: () => {
315 | wx.createSelectorQuery()
316 | .selectAll(".a-class")
317 | .boundingClientRect((rects: wx.NodesRefRect[]) => {
318 | rects.forEach(rect => {
319 | rect.id; // 节点的ID
320 | rect.dataset; // 节点的dataset
321 | rect.left; // 节点的左边界坐标
322 | rect.right; // 节点的右边界坐标
323 | rect.top; // 节点的上边界坐标
324 | rect.bottom; // 节点的下边界坐标
325 | rect.width; // 节点的宽度
326 | rect.height; // 节点的高度
327 | });
328 | })
329 | .exec();
330 | }
331 | });
332 |
333 | const recorderManager = wx.getRecorderManager();
334 |
335 | recorderManager.onStart(() => {
336 | console.log("recorder start");
337 | });
338 | recorderManager.onResume(() => {
339 | console.log("recorder resume");
340 | });
341 | recorderManager.onPause(() => {
342 | console.log("recorder pause");
343 | });
344 | recorderManager.onStop(res => {
345 | console.log("recorder stop", res);
346 | const { tempFilePath } = res;
347 | });
348 | recorderManager.onFrameRecorded(res => {
349 | const { frameBuffer } = res;
350 | console.log("frameBuffer.byteLength", frameBuffer.byteLength);
351 | });
352 |
353 | const options = {
354 | duration: 10000,
355 | sampleRate: 44100,
356 | numberOfChannels: 1,
357 | encodeBitRate: 192000,
358 | format: "aac",
359 | frameSize: 50
360 | };
361 |
362 | recorderManager.start(options);
363 |
364 | wx.onGetWifiList(res => {
365 | if (res.wifiList.length) {
366 | wx.setWifiList({
367 | wifiList: [
368 | {
369 | SSID: res.wifiList[0].SSID,
370 | BSSID: res.wifiList[0].BSSID,
371 | password: "123456"
372 | }
373 | ]
374 | });
375 | } else {
376 | wx.setWifiList({
377 | wifiList: []
378 | });
379 | }
380 | });
381 | wx.getWifiList();
382 |
383 | wx.onWifiConnected(wifi => {
384 | // wifi.BSSID
385 | });
386 |
387 | wx.getConnectedWifi({
388 | success(result) {
389 | result.signalStrength;
390 | }
391 | });
392 |
393 | wx.getWeRunData({
394 | success(res) {
395 | const encryptedData = res.encryptedData;
396 | }
397 | });
398 |
399 | const uploadTask = wx.uploadFile({
400 | url: "http://example.weixin.qq.com/upload", // 仅为示例,非真实的接口地址
401 | filePath: "/local/folder/file.ext",
402 | name: "file",
403 | formData: {
404 | user: "test"
405 | },
406 | success: res => {
407 | const data = res.data;
408 | // do something
409 | }
410 | });
411 |
412 | uploadTask.onProgressUpdate(res => {
413 | console.log("上传进度", res.progress);
414 | console.log("已经上传的数据长度", res.totalBytesSent);
415 | console.log("预期需要上传的数据总长度", res.totalBytesExpectedToSend);
416 | });
417 |
418 | uploadTask.abort(); // 取消上传任务
419 |
420 | const downloadTask = wx.downloadFile({
421 | url: "http://example.com/audio/123", // 仅为示例,并非真实的资源
422 | success: res => {
423 | wx.playVoice({
424 | filePath: res.tempFilePath
425 | });
426 | }
427 | });
428 |
429 | downloadTask.onProgressUpdate(res => {
430 | console.log("下载进度", res.progress);
431 | console.log("已经下载的数据长度", res.totalBytesWritten);
432 | console.log("预期需要下载的数据总长度", res.totalBytesExpectedToWrite);
433 | });
434 |
435 | downloadTask.abort(); // 取消下载任务
436 |
437 | wx.request({
438 | url: "https://www.baidu.com",
439 | method: "GET",
440 | success(res) {
441 | if (res.statusCode < 300) {
442 | console.log(res.data);
443 | } else {
444 | console.warn(res.statusCode, res.header);
445 | }
446 | },
447 | fail(e) {
448 | console.error(e);
449 | }
450 | }).abort();
451 |
452 | wx.getSystemInfo({
453 | success(res) {
454 | const {
455 | brand,
456 | pixelRatio,
457 | platform,
458 | windowHeight,
459 | windowWidth,
460 | screenHeight,
461 | screenWidth,
462 | statusBarHeight,
463 | SDKVersion,
464 | language,
465 | model,
466 | version,
467 | fontSizeSetting,
468 | system
469 | } = res;
470 | }
471 | });
472 |
473 | function testAccountInfo(): string {
474 | const accountInfo: wx.AccountInfo = wx.getAccountInfoSync();
475 | return accountInfo.miniProgram.appId;
476 | }
477 |
478 | wx.reportAnalytics("test-event", { a: 1, b: "2" });
479 |
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/IconsLoader.java:
--------------------------------------------------------------------------------
1 | package com.ytw88.weappsupport;
2 |
3 | import com.intellij.openapi.util.IconLoader;
4 |
5 | import javax.swing.*;
6 |
7 | public class IconsLoader {
8 | public final static Icon WEAPP_ICON = IconLoader.findIcon("/icons/weapp.png");
9 |
10 | public final static Icon WXML_ICON = IconLoader.findIcon("/icons/wxml.png");
11 |
12 | public final static Icon WXSS_ICON = IconLoader.findIcon("/icons/wxss.png");
13 | }
14 |
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/WeappFramework.java:
--------------------------------------------------------------------------------
1 | package com.ytw88.weappsupport;
2 |
3 | import com.intellij.framework.FrameworkTypeEx;
4 | import com.intellij.framework.addSupport.FrameworkSupportInModuleConfigurable;
5 | import com.intellij.framework.addSupport.FrameworkSupportInModuleProvider;
6 | import com.intellij.ide.util.frameworkSupport.FrameworkSupportModel;
7 | import com.intellij.openapi.module.Module;
8 | import com.intellij.openapi.module.ModuleType;
9 | import com.intellij.openapi.roots.ModifiableModelsProvider;
10 | import com.intellij.openapi.roots.ModifiableRootModel;
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | import javax.swing.*;
15 |
16 | public class WeappFramework extends FrameworkTypeEx {
17 | public static final String FRAMEWORK_ID = "Weapp";
18 |
19 | protected WeappFramework() {
20 | super(FRAMEWORK_ID);
21 | }
22 |
23 | @NotNull
24 | @Override
25 | public FrameworkSupportInModuleProvider createProvider() {
26 | return new FrameworkSupportInModuleProvider() {
27 | @NotNull
28 | @Override
29 | public FrameworkTypeEx getFrameworkType() {
30 | return WeappFramework.this;
31 | }
32 |
33 | @NotNull
34 | @Override
35 | public FrameworkSupportInModuleConfigurable createConfigurable(@NotNull FrameworkSupportModel frameworkSupportModel) {
36 | return new FrameworkSupportInModuleConfigurable() {
37 | @Nullable
38 | @Override
39 | public JComponent createComponent() {
40 | return new JCheckBox("Extra Option");
41 | }
42 |
43 | @Override
44 | public void addSupport(@NotNull Module module, @NotNull ModifiableRootModel modifiableRootModel, @NotNull ModifiableModelsProvider modifiableModelsProvider) {
45 | //do what you want here: setup a library, generate a specific file, etc
46 | //TODO 这里也许可以将weapp的library加入到js library中
47 | }
48 | };
49 | }
50 |
51 | @Override
52 | public boolean isEnabledForModuleType(@NotNull ModuleType moduleType) {
53 | return (moduleType instanceof WeappModuleType);
54 | }
55 | };
56 | }
57 |
58 | @NotNull
59 | @Override
60 | public String getPresentableName() {
61 | return "WeApp Framework";
62 | }
63 |
64 | @NotNull
65 | @Override
66 | public Icon getIcon() {
67 | return IconsLoader.WEAPP_ICON;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/WeappJSPredefinedLibraryProvider.java:
--------------------------------------------------------------------------------
1 | package com.ytw88.weappsupport;
2 |
3 | import com.intellij.lang.javascript.library.JSPredefinedLibraryProvider;
4 | import com.intellij.openapi.diagnostic.Logger;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.vfs.VfsUtil;
7 | import com.intellij.openapi.vfs.VirtualFile;
8 | import com.intellij.webcore.libraries.ScriptingLibraryModel;
9 | import org.jetbrains.annotations.NonNls;
10 | import org.jetbrains.annotations.NotNull;
11 |
12 | import java.net.URL;
13 | import java.util.ArrayList;
14 | import java.util.HashSet;
15 | import java.util.List;
16 | import java.util.Set;
17 |
18 | public class WeappJSPredefinedLibraryProvider extends JSPredefinedLibraryProvider {
19 | @NonNls
20 | private static final String LIBRARY_NAME = "weapp-api";
21 |
22 | @NonNls
23 | private String[] jsFiles = {
24 | "/lib/weixin-app/index.d.ts",
25 | "/lib/weixin-app/tsconfig.json",
26 | "/lib/weixin-app/tslint.json",
27 | "/lib/weixin-app/weixin-app-tests.ts"
28 | };
29 |
30 | @NotNull
31 | @Override
32 | public ScriptingLibraryModel[] getPredefinedLibraries(@NotNull Project project) {
33 | Set virtualFiles = getVirtualFiles();
34 |
35 | ScriptingLibraryModel scriptingLibraryModel = ScriptingLibraryModel.createPredefinedLibrary(LIBRARY_NAME,
36 | virtualFiles.toArray(new VirtualFile[virtualFiles.size()]), true);
37 |
38 | return new ScriptingLibraryModel[]{scriptingLibraryModel};
39 | }
40 |
41 | @NotNull
42 | @Override
43 | public Set getRequiredLibraryFilesToIndex() {
44 | return getVirtualFiles();
45 | }
46 |
47 | @NotNull
48 | @Override
49 | public Set getRequiredLibraryFilesForResolve(@NotNull Project project) {
50 | return getVirtualFiles();
51 | }
52 |
53 | private Set getVirtualFiles() {
54 | Set virtualFiles = new HashSet<>();
55 |
56 | final List names = new ArrayList();
57 | for (String libFileName : this.jsFiles) {
58 | URL fileUrl = WeappJSPredefinedLibraryProvider.class.getResource(libFileName);
59 | names.add(fileUrl.toString());
60 | virtualFiles.add(VfsUtil.findFileByURL(fileUrl));
61 | }
62 | return virtualFiles;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/WeappModuleBuilder.java:
--------------------------------------------------------------------------------
1 | package com.ytw88.weappsupport;
2 |
3 | import com.intellij.ide.util.projectWizard.ModuleBuilder;
4 | import com.intellij.ide.util.projectWizard.ModuleWizardStep;
5 | import com.intellij.ide.util.projectWizard.WizardContext;
6 | import com.intellij.openapi.Disposable;
7 | import com.intellij.openapi.module.ModuleType;
8 | import com.intellij.openapi.options.ConfigurationException;
9 | import com.intellij.openapi.roots.ModifiableRootModel;
10 | import com.intellij.openapi.roots.libraries.LibraryTable;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | public class WeappModuleBuilder extends ModuleBuilder {
14 | @Override
15 | public void setupRootModel(ModifiableRootModel modifiableRootModel) throws ConfigurationException {
16 | //TODO ....
17 | LibraryTable libraryTable = modifiableRootModel.getModuleLibraryTable();
18 | libraryTable.createLibrary();
19 | }
20 |
21 | @Override
22 | public ModuleType getModuleType() {
23 | return WeappModuleType.getInstance();
24 | }
25 |
26 | @Nullable
27 | @Override
28 | public ModuleWizardStep getCustomOptionsStep(WizardContext context, Disposable parentDisposable) {
29 | return new WeappModuleWizardStep();
30 | }
31 |
32 | @Nullable
33 | @Override
34 | public String getBuilderId() {
35 | return "weapp";
36 | }
37 |
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/WeappModuleType.java:
--------------------------------------------------------------------------------
1 | package com.ytw88.weappsupport;
2 |
3 | import com.intellij.ide.util.projectWizard.ModuleWizardStep;
4 | import com.intellij.ide.util.projectWizard.WizardContext;
5 | import com.intellij.openapi.module.ModuleType;
6 | import com.intellij.openapi.module.ModuleTypeManager;
7 | import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import javax.swing.*;
11 |
12 | public class WeappModuleType extends ModuleType {
13 | public static final String MODULE_ID = "WEAPP_MODULE";
14 |
15 | public static WeappModuleType getInstance() {
16 | return (WeappModuleType) ModuleTypeManager.getInstance().findByID(MODULE_ID);
17 | }
18 |
19 | public WeappModuleType() {
20 | super(MODULE_ID);
21 | }
22 |
23 | @NotNull
24 | @Override
25 | public WeappModuleBuilder createModuleBuilder() {
26 | return new WeappModuleBuilder();
27 | }
28 |
29 | @NotNull
30 | @Override
31 | public String getName() {
32 | return "WeChat App";
33 | }
34 |
35 | @NotNull
36 | @Override
37 | public String getDescription() {
38 | return "Weapp module";
39 | }
40 |
41 | @Override
42 | public Icon getNodeIcon(boolean b) {
43 | return IconsLoader.WEAPP_ICON;
44 | }
45 |
46 | @NotNull
47 | @Override
48 | public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, @NotNull WeappModuleBuilder moduleBuilder, @NotNull ModulesProvider modulesProvider) {
49 | return super.createWizardSteps(wizardContext, moduleBuilder, modulesProvider);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/WeappModuleWizardStep.java:
--------------------------------------------------------------------------------
1 | package com.ytw88.weappsupport;
2 |
3 | import com.intellij.ide.util.projectWizard.ModuleWizardStep;
4 |
5 | import javax.swing.*;
6 |
7 | public class WeappModuleWizardStep extends ModuleWizardStep {
8 | @Override
9 | public JComponent getComponent() {
10 | return new JLabel("hahahaha");
11 | }
12 |
13 | @Override
14 | public void updateDataModel() {
15 | //todo update model according to UI
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/WeappProjectStructureDetector.java:
--------------------------------------------------------------------------------
1 | package com.ytw88.weappsupport;
2 |
3 | import com.intellij.ide.util.importProject.ProjectDescriptor;
4 | import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
5 | import com.intellij.ide.util.projectWizard.importSources.ProjectFromSourcesBuilder;
6 | import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | import java.io.File;
10 | import java.util.Collection;
11 | import java.util.List;
12 |
13 | public class WeappProjectStructureDetector extends ProjectStructureDetector {
14 | @NotNull
15 | @Override
16 | public DirectoryProcessingResult detectRoots(@NotNull File file, @NotNull File[] files, @NotNull File file1, @NotNull List list) {
17 | return null;
18 | }
19 |
20 | @Override
21 | public String getDetectorId() {
22 | return "Weapp";
23 | }
24 |
25 | @Override
26 | public void setupProjectStructure(@NotNull Collection roots, @NotNull ProjectDescriptor projectDescriptor, @NotNull ProjectFromSourcesBuilder builder) {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/WeappStartupActivity.java:
--------------------------------------------------------------------------------
1 | package com.ytw88.weappsupport;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.startup.StartupActivity;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | public class WeappStartupActivity implements StartupActivity {
8 | @Override
9 | public void runActivity(@NotNull Project project) {
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/wxml/WxmlColorSettingsPage.java:
--------------------------------------------------------------------------------
1 | package com.ytw88.weappsupport.wxml;
2 |
3 | import com.intellij.openapi.editor.colors.TextAttributesKey;
4 | import com.intellij.openapi.fileTypes.SyntaxHighlighter;
5 | import com.intellij.openapi.options.colors.AttributesDescriptor;
6 | import com.intellij.openapi.options.colors.ColorDescriptor;
7 | import com.intellij.openapi.options.colors.ColorSettingsPage;
8 | import com.ytw88.weappsupport.IconsLoader;
9 | import org.jetbrains.annotations.NotNull;
10 | import org.jetbrains.annotations.Nullable;
11 |
12 | import javax.swing.*;
13 | import java.util.Map;
14 |
15 | public class WxmlColorSettingsPage implements ColorSettingsPage {
16 | private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[]{
17 | new AttributesDescriptor("Comment", WxmlSyntaxHighlighter.COMMENT),
18 | };
19 |
20 | @Nullable
21 | @Override
22 | public Icon getIcon() {
23 | return IconsLoader.WXML_ICON;
24 | }
25 |
26 | @NotNull
27 | @Override
28 | public SyntaxHighlighter getHighlighter() {
29 | return new WxmlSyntaxHighlighter();
30 | }
31 |
32 | @NotNull
33 | @Override
34 | public String getDemoText() {
35 | return "\n" +
36 | "\n" +
37 | " > (attrs | rule) {
60 | pin=1 recoverWhile=grammar_element_recover
61 | }
62 | private grammar_element_recover::=!('{'|rule_start)
63 |
64 | rule ::= rule_start expression attrs? ';'? {pin=2}
65 | private rule_start ::= modifier* id '::='
66 | modifier ::= 'private' | 'external' | 'meta'
67 | | 'inner' | 'left' | 'upper' | 'fake'
68 |
69 | attrs ::= '{' attr * '}' {pin=1}
70 | attr ::= attr_start attr_value ';'? {
71 | pin=1 recoverWhile=attr_recover
72 | }
73 | private attr_start ::= id (attr_pattern '=' | '=') {
74 | pin(".*")="attr_pattern"
75 | }
76 | private attr_start_simple ::= id attr_pattern? '='
77 | private attr_recover ::= !('}' | attr_start)
78 | private attr_value ::= attr_value_inner !'='
79 | private attr_value_inner ::= reference_or_token
80 | | literal_expression
81 | | value_list
82 | attr_pattern ::= '(' string_literal_expression ')' {
83 | pin=1 methods=[literalExpression="string_literal_expression"]
84 | }
85 |
86 | value_list ::= '[' list_entry * ']' {pin=1 extends=expression}
87 | list_entry ::= (id list_entry_tail? | string_literal_expression) ';'? {
88 | recoverWhile=list_entry_recover
89 | methods=[getReferences literalExpression="string_literal_expression"]
90 | }
91 | private list_entry_tail ::= '=' string_literal_expression {pin=1}
92 | private list_entry_recover ::= !(']' | '}' | id | string)
93 |
94 | expression ::= sequence choice?
95 | sequence ::= option * {
96 | extends=expression recoverWhile=sequence_recover
97 | }
98 | private sequence_recover ::=
99 | !(';'|'|'|'('|')'|'['|']'|'{'|'}') grammar_element_recover
100 | private option ::= predicate | paren_opt_expression | simple quantified?
101 |
102 | left choice ::= ( '|' sequence ) + {pin(".*")=1 extends=expression}
103 | left quantified ::= quantifier {extends=expression }
104 | quantifier ::= '?' | '+' | '*'
105 |
106 | predicate ::= predicate_sign simple {extends=expression}
107 | predicate_sign ::= '&' | '!'
108 |
109 | fake parenthesized ::= '(' expression ')' {extends=expression}
110 | private simple ::= !(modifier* id '::=' ) reference_or_token
111 | | literal_expression
112 | | external_expression
113 | | paren_expression
114 | external_expression ::= '<<' reference_or_token option * '>>' {
115 | pin=2 extends=expression methods=[refElement='/expression[0]' getArguments]
116 | }
117 | reference_or_token ::= id {extends=expression methods=[resolveRule]}
118 | literal_expression ::= string_literal_expression | number {extends=expression }
119 | string_literal_expression ::= string {extends=literal_expression}
120 | paren_expression ::= '(' expression ')' | '{' alt_choice_element '}' {pin(".*")=2}
121 | paren_opt_expression ::= '[' expression ']' {pin=2}
122 | private alt_choice_element ::= !attr_start_simple expression
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/wxml/grammer/tutorial2.bnf:
--------------------------------------------------------------------------------
1 | {
2 | tokens=[
3 | SEMI=';'
4 | EQ='='
5 | LP='('
6 | RP=')'
7 |
8 | space='regexp:\s+'
9 | comment='regexp://.*'
10 | number='regexp:\d+(\.\d*)?'
11 | id='regexp:\p{Alpha}\w*'
12 | string="regexp:('([^'\\]|\\.)*'|\"([^\"\\]|\\.)*\")"
13 |
14 | op_1='+'
15 | op_2='-'
16 | op_3='*'
17 | op_4='/'
18 | op_5='!'
19 | ]
20 |
21 | name(".*expr")='expression'
22 | extends(".*expr")=expr
23 | }
24 |
25 | root ::= root_item *
26 | private root_item ::= !<> property ';' {pin=1 recoverWhile=property_recover}
27 |
28 | property ::= id '=' expr {pin=2}
29 | private property_recover ::= !(';' | id '=')
30 |
31 | expr ::= factor plus_expr *
32 | left plus_expr ::= plus_op factor
33 | private plus_op ::= '+'|'-'
34 | private factor ::= primary mul_expr *
35 | left mul_expr ::= mul_op primary
36 | private mul_op ::= '*'|'/'
37 | private primary ::= primary_inner factorial_expr ?
38 | left factorial_expr ::= '!'
39 | private primary_inner ::= literal_expr | ref_expr | paren_expr
40 | paren_expr ::= '(' expr ')' {pin=1}
41 | ref_expr ::= id
42 | literal_expr ::= number | string | float
--------------------------------------------------------------------------------
/src/com/ytw88/weappsupport/wxml/grammer/wxml.bnf:
--------------------------------------------------------------------------------
1 | {
2 | // Name and the location of the parser which will be generated.
3 | parserClass="com.ytw88.weappsupport.wxml.parser.WxmlParser"
4 | parserUtilClass="com.ytw88.weappsupport.wxml.parser.WxmlParserUtil"
5 |
6 | // All nodes will extend this class. This wraps AST node to a PSI node.
7 | extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
8 |
9 | // Prefix for all generated classes.
10 | psiClassPrefix="Wxml"
11 | // Suffix for implementation classes.
12 | psiImplClassSuffix="Impl"
13 |
14 | // Location to be used when generating PSI classes.
15 | psiPackage="com.ytw88.weappsupport.wxml.psi"
16 | // Location to be used when generating PSI implementation classes.
17 | psiImplPackage="com.ytw88.weappsupport.wxml.psi.impl"
18 |
19 | // Element type holder class name.
20 | elementTypeHolderClass="com.ytw88.weappsupport.wxml.psi.WxmlTypes"
21 |
22 | // Class which will be used to create internal nodes.
23 | elementTypeClass="com.ytw88.weappsupport.wxml.psi.WxmlElementType"
24 | // Class which will be used to create leaf nodes.
25 | tokenTypeClass="com.ytw88.weappsupport.wxml.psi.WxmlTokenType"
26 |
27 | tokens = [
28 | Eq = '='
29 | S = 'regexp:\s+'
30 |
31 | Comment = 'regexp:\