2019年如何撸一个Cloud Stuido插件?

纳尼?2019年?其实一开始我的内心是拒绝的,一转眼就到了2018年的尾声,再不写个【2019年】的标题定好文章的保质期,感觉刚写这篇文章就要过期了!然而,回顾2018年,每每想起2018年的收获,我的内心是奔溃的,又是一无所获的一年悄然虚度;看着镜子里头发和技术一样稀疏的自己,我也只能认命接受--“少壮不努力,老大偷代码”……废话写了一大堆,下面,咳咳,下面进入正题吧:

什么是CS?

当我问公司前端组的同事玩不玩CS的时候,他一脸懵逼地反问你是说“CF”吗,我忘了这些年轻人可能都没玩过“反恐精英(Counter-Strike)”,随后给他们丢了一个链接--Cloud Studio。Cloud Studio,是2018年4月上线的一款基于浏览器的集成式开发环境(IDE),开发者可以在 Cloud Studio 中创建工作空间来存放自己的项目代码,安装所需要的环境,以及运行、编译自己的项目,实现了“打开浏览器 就能写代码”。最新的Cloud Studio已升级至腾讯云开发者平台中,并提供了一个永不间断的云端工作站,也就意味着在Cloud Studio新建的项目可直接托管在腾讯云开发者平台,还有强大的插件系统、一键切换开发环境、一键切换开发环境、全功能终端以及实时进度保存等特色功能。本文主要讲如何“Copy”出一个自定义的插件实现个性化需求。

什么是Cloud Studio Plugins

Cloud Studio Plugins是Cloud Studio这个IDE的可自定义开发的插件系统,您也许用过其他IDE的拓展功能,Cloud Studio 也开放了这个拓展功能--插件系统,通过插件您可以自定义自己的工作空间,提升使用效率,您也可以前往插件市场安装自己喜欢的插件,甚至还可以开发一款自己喜欢的插件,给所有用户使用。由于Cloud Studio基于 Monaco Editor 以及 React ,在插件的开发过程中能看到VS Code的影子,或者涉及到一些用React写的功能。

  • 插件市场
    插件市场可以理解为菜市场,菜市场里有各种类型的菜,这些菜吃了都会有一定的效果,比喻写代码更顺手,当然也有些可能有毒,比如把您变成矿机、盗取您的比特币。为了避免被毒害,我们自己也可以成为菜农实现自给自足。CS的插件市场目前分为Git 功能增强、Git UI 增强、编辑器功能增强、编辑器 UI 增强、代码片段、语法高亮、快捷键、编辑器预览视图、文件图标主题、娱乐小工具、信息栏显示内容、腾讯云接口、第三方 API、实用类小工具,总的一点就是实用的和好玩的。

    Cloud Studio插件市场

  • 开发文档
    对于我这种老菜鸟来说,上手一个新技术、新工具,最快的捷径就是先把相关技术的文档过一遍,不过有些时候,文档也不是万能的,比如文档不详细,这个时候就只有自己多用多思考多尝试。Cloud Studio的插件文档就是不很全面,开放出来的API比较少,不过良心所在,文档明确告知Cloud Studio 的编辑器部分使用微软开源的 [Monaco Editor](https://github.com/Microsoft/monaco-editor),这也是 Visual Studio Code 的核心编辑器,通过 editor 模块的相关 API 获取到编辑器的实例,也就是说可以参照VS Code的插件开发,编辑器这一层的API大部分是通用的。

    Cloud Studio Plugins 开发文档

Just Do It

前辈说:“看一百遍。不如动手写一遍”,在编程的学习过程中,“就是干!”很适合我这种老菜鸟,回顾不长不短的职业生涯,之所以还是菜鸟,归结到底就是动手太少了,文档、视频课看了不少,实操太少了,导致看过的基本忘却,以一个典型的反面教材,告诫2019年即将踏入编码界的新同学们,2019年“就是干”,多撸代码少看片。废话又说了一大堆,先根据官方文档撸个[HelloWorld]

环境准备

虽然说,Cloud Studio最大的特色是可以在浏览器中完成开发工作,但起码,您得有个浏览器环境吧,对于前端来说,Chrome 可能是使用最多的。然后得拥有Cloud Studio账号,由于刚对接到腾讯云的缘故,注册账号到新建工作空间这一块是有个巨坑的,首先是微信授权后各种跳转(自动跳转),然后是要求先改用户名(又是各种跳转),再然后是认证之类的,反正相当于您得先有一个腾讯云的账号用来存放代码,最后一步也是有坑的,新建的工作空间已经生成了git仓库但就是没同步过来,经过一番删cookie之类的操作之后,终于能开始书写第一个Cloud Studio插件了!

Cloud Studio Start

初始化

新建一个插件的时候,工具会新建一个基础环境(Node.js,版本 v8.12.0)和基础代码文件,各个文件具体可以看看文档,都有详细说明,当然这些文件是可以在腾讯云开发者平台里面找到的,并且可以通过git下载到本地,虽然本地不方便调试插件,但是本地开发符合产品开发流程,在没网或弱网的时候不会出现文件保存失败或缺失的情况(在线编辑器都有点小毛病,尽管用了大量的缓存技术),另外也方便协同开发。

Cloud Studio 代码仓库

新建的这个初始化环境和代码是可以直接跑起来的,代码是用的React,写了一个底部状态栏的按钮,点击右侧可提示通知。按照工程化的思想,我们开发的代码一般写在src目录里边,其他的基本都是配置文件,虽然demo推荐使用React,但也不是必须的,我看插件市场有些大佬也是直接用的js,总之您自己玩得开心就行。
image

目录结构

│  .babelrc                          // bable配置文件
│  .eslintrc                         // eslint配置文件
│  .gitignore                        // git忽略配置文件
│  .npmrc                            // npm配置文件
│  cloudstudio.d.ts                  // CS配置文件
│  cloudstudio.png                   // CS logo
│  package.json                      // 包文件
│  README.md                         // 您的插件文档
│  tsconfig.json                     // ts配置
│  
├─config                             // 自定义webpack
│      webpack.dev.config.js
│      webpack.production.config.js
│      
├─src                                // 源码目录
│      index.tsx
│      
└─typings
        monaco.d.ts                  // CS内核monaco配置

以上,一般的开发过程中,我们只需关注Scr目录和修改README.md文件,就是愉快的完成一个Cloud Studio的插件,大佬就随意修改啦,当然小白也有特殊情况:刚好您写了测试代码test目录,且下面包含.tsx文件,可能会遇到typescript报错,这时就需要修改typescript.json,比如我遇到的:


Cloud Studio typescript 报错

解决:忽略test目录


修改 typescript.json

Hello CloudStudio 源码浅析

● src/index.tsx

    // 引入插件开发sdk(https://github.com/Coding/WebIDE-Plugin-SDK)
    import Manager from 'webide-plugin-sdk/Manager';
    // 引入React
    import * as React from 'react';
    // 引入相关API的类和函数
    import { actions, PluginContext, appRegistry } from 'cloudstudio';
    // 初始化插件上下文
    const context = new PluginContext({});
    
    // 插件唯一 key
    // 一定要确保代码中 pluginKey 的唯一性,推荐使用「开发者邮箱-昵称-插件名」的格式
    const pluginKey = 'hello-widget-plugin';
    // 界面引入、卸载插件
    const { position, inject, unregister } = context.injectComponent;
    // Hello组件
    class Hello extends React.Component {
        // 弹出一个 Hello CloudStudio! 通知
        handleClick = () => {
            actions.notification({ message: 'Hello CloudStudio!', notifyType: 'INFO' });
        }
        // 渲染sayHello 按钮
        render() {
            return (
                <button onClick={this.handleClick}>sayHello</button>
            )
        }
    }
    
    class MyManager extends Manager {
        pluginWillMount() {
            // 状态栏注入组件
            inject(
                position.STATUSBAR.WIDGET,
                {
                    key: pluginKey,
                },
                extension => {
                    return React.createElement(extension.app);
                }
            )
        }
    
        pluginWillUnmount() {
            // 卸载组件
            unregister({ key: pluginKey, app: Hello, position: position.STATUSBAR.WIDGET });
        }
    }
    
    // 注册插件
    appRegistry([{
        Manager: MyManager,
        key: pluginKey,
        app: Hello,
    }]);

其实也没什么好说明的,无非就是一些常用的API的使用,按照官方文档的规范来开发,能避免一些莫名其妙的bug。虽然这个dome只实现了点击弹出通知框,但还是实现了从0到1的过程,尽管如此,还是有些插件直接用的这个demo,一样的配方熟悉的味道(别中毒),比如把弹出框的内容改成通过调用第三方api返回的数据,就成了一言这个插件:

handleClick = () => {
    axios.get('https://v1.hitokoto.cn/?encode=text')
    .then(res => {actions.notification({ message: res.data, notifyType: 'INFO' })})
}

同样我也可以改成来个段子:

handleClick = () => {
    let message = ""
    axios.get("https://api.apiopen.top/getJoke?count=1&type=text")
    .then(function (response) {
        message = response.data.result[0].text;
        actions.notification({ message: message, notifyType: 'INFO' });
    })
    .catch(function (error) {
        actions.notification({ message: error, notifyType: 'INFO' });
    });
}

还可以是享受宁静:

handleClick = () => {
    audio_flag = !audio_flag;
    if(audio_flag)
        actions.notification({ message: '开始享受宁静吧!', notifyType: 'INFO' })
    else
        actions.notification({ message: '贤者模式!', notifyType: 'INFO' })

       
    audio.src = rain;
    audio.autoplay = !audio.autoplay;
    audio.addEventListener('timeupdate',function(){
        var num = 1.4;
        if(this.currentTime > this.duration - num){
            this.currentTime = 0;
            this.play();
        }
    },false);
}

是不是感觉Cloud Studio Plugins 乳此简单?
总之,多看文档,多写,多思考!

(呃,就写完一个CS插件了?结束了?不是说还可以把VS Code的插件拿过来用吗?)

复用VS Code插件实现CS插件

刚开始接触Cloud Studio的时候,有大佬就提出过把vscode的热门插件搬运过来,或者写个工具--插件转换器,刚好在入门React,本地的VS Code上常用Reactjs code snippets,索性直接搬过来"写"一个Cloud studio的React代码片段插件,于是就有了CS版的React Snippets(欢迎拍砖指正)。
不难发现核心文件就是这个json文件,通过快捷方式输出相应的代码块,代码片段的插件都可以使用这种方式来开发。

源码开发

  • snippets.json:


    React Snippets

    通过format.js对json进行格式化处理,并输出:

      //snippets.json format
      const fs = require('fs')
      
      const files = fs.readdirSync('/home/coding/workspace/src/snippets')
      for(var i = 0; i < files.length; i++) {
          justFormat(files[i])
      }
      
      function justFormat (file) {
          var before = fs.readFileSync(`/home/coding/workspace/src/snippets/${file}`, 'utf8')
          before = JSON.parse(before)
      
          var after = []
          for (var k in before) {
              after.push({
                  "label": before[k]['prefix'],
                  "insertText": {
                      "value": (() => {
                          const body = before[k]['body']
                          let str = ''
                          for(var i = 0; i < body.length; i++) {
                              str += `${body[i]}`
                          }
                          return str
                      })()
                  },
                  "documentation": before[k]['description']
              })
          }
          // console.log(before, after)
          fs.writeFileSync(`/home/coding/workspace/src/${file}`, JSON.stringify(after))
          console.log('success: ', file)
      }
    

index.jsx注册插件:

import Manager from 'webide-plugin-sdk/Manager';
import { actions, PluginContext, appRegistry, editor} from 'cloudstudio';
import reactSnippets from './snippets.json';

const context = new PluginContext({});

// 插件唯一 key
// 一定要确保代码中 pluginKey 的唯一性,推荐使用「开发者邮箱-昵称-插件名」的格式
const pluginKey = 'huqi@fashaoge.com-huqi-reactSnippets';

const { position, inject, unregister } = context.injectComponent;

class MyManager extends Manager {
    pluginWillMount() {
         editor.registerCodeSnippetsProvider(['javascript', 'typescript'], reactSnippets);
    }

    pluginWillUnmount() {
        // 卸载组件
        unregister({ key: pluginKey,  position: position.STATUSBAR.WIDGET });
    }
}

appRegistry([{
    Manager: MyManager,
    key: pluginKey
}]);

至此,一个Cloud Studio 的React代码片段的插件源码开发部分就完成了

插件调试与发布

调试是确保程序能正常预期运行,Cloud Studio的插件文档提出了两种调试方法,我愚笨,第二种硬是没找到入口,当然,第一种已经满足了,只是有些莫名其妙的bug,调试过程中弹窗可能不会执行。

  • 执行yarn 和 yarn start
    yarn
    yarn start
    如果没有错误就直接显示Success
    `
    ➜ workspace git:(master) yarn start
    yarn run v1.12.3
    coding-ide start(pwd)

Compiling

✔ success cloudstudio-plugin-sdk compiled in 2s 243ms
Time: 2248ms
chunk {0} index.js (main) 15.3 kB [entry] [rendered]
webpack: Compiled successfully.
`

  • 点击右侧插件开发可加载当前插件并进行调试


    React Snippets

    我这里提示警告是因为这个pluginKey的插件已经存在

  • 点击右侧插件开发可加载当前插件并进行调试

    插件编写完成后先将代码push到git仓库,然后在插件开发这个选项卡点击发布插件可跳转到发布界面

      `
      git add .
      git commit -m 'push'
      git push
      `
    

    请一定要将你您的代码 push 到远端仓库,再发布插件

    React Snippets

最后就静静的等待审核!

期待能在插件市场"吃到您的放心菜"!

后记

开发一个CS插件并不难,难就难在怎样开发一个高质量的CS插件,一是方便自己,二是普惠大众。通过对Cloud Studio 的使用,以及对Cloud studio 插件开发的尝试,我对“就是干”的学习格言又有了新的认识,如果没有尝试,可能我会觉得开发插件是件多么难的事情,可能还会导致我不敢去尝试接下来会遇到的一些新框架、新工具。唯一不变的也许只有不变,短短几年时间,感觉jQuery是远古时代的神兽了,而三大主流框架以及各种层出不穷的新框架、新工具、思想,逼迫我们不得不学,“不进而退”,不存在“学不动”,“学不动”只会被淘汰。最后,向xabikos以及其他开发者致歉,对我“写”React Snippets这个Cloud Studio 插件的剽窃行为致歉。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,481评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,691评论 2 59
  • not to be unnoticed 被忽略,未被注意 hardly, nearly 连用表示双重否定 句式: ...
    你好Ruby阅读 183评论 0 0
  • rpx单位是微信小程序中css的尺寸单位,rpx可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPh...
    奋斗1216阅读 392评论 0 2