GithubHelp home page GithubHelp logo

learn-vscode-extension's Introduction

Hi,我是彭道宽! 👋

就职于 CVTE,江湖人称 “彭于晏广州分晏”,掘金LV5博主,掘金小册作者,投身开源,rc-redux-model 库作者,SugarTurboS 开源组织推动者之一,坚持用心写文章。你可以在下面找到我

社区主页

基本信息

⚡ Technologies

These are some of the technologies and tools that I work with:

JavaScript Nodejs Less TypeScript Taro React Vue Git GitHub VSCode

learn-vscode-extension's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

learn-vscode-extension's Issues

10.自定义插件首选项配置,根据配置执行不同逻辑—例子

场景

每一个插件都可以自行添加首选项的配置,当打开 vscode 时,根据首选项选择的配置,执行不同的逻辑,接下来说说如何实现此效果

代码展示

我们现在 package.json 中配置一下我们首选项参数

{
   "contributes": {
      "configuration": {
        "title": "sugar-demo-vscod",
        "properties": {
          "sugar-demo-vscode.matchConfig": {
            "type": "string",
            "description": "sugar-demo-vscod 配置,默认低配版本",
            "enum": [
              "lowMatch",
              "middleMatch",
              "highMatch"
            ],
            "default": "lowMatch",
            "scope": "window"
          }
        }
      }
  }
}

这里需要注意,名称 sugar-demo-vscode 要一致!

上面我们已经实现了首选项配置,看看效果

我们再获取配置,然后执行不同逻辑

    // 获取用户配置的版本设置
    const matchConfig = vscode.workspace.getConfiguration().get('vscode-beehive-extension.matchConfig')
    if (matchConfig === MATCH_CONFIG_MAPS.LOW) {
       console.log('低配')
    } else if (matchConfig === MATCH_CONFIG_MAPS.MIDDLE) {
        console.log('中配')
    } else if (matchConfig === MATCH_CONFIG_MAPS.HIGH) {
        console.log('高配')
    } else {
      vscode.window.showErrorMessage(`unknown error`)
    }

如果要通过代码修改 matchConfig 内容,可以通过

// 最后一个参数,为true时表示写入全局配置,为false或不传时则只写入工作区配置
vscode.workspace.getConfiguration().update('vscode-beehive-extension.matchConfig', 'middleMatch, true);

源码展示

03. 自定义WebView欢迎页面-例子

场景

该插件场景适合做一些简单页面展示、欢迎页面等

代码展示

// demo3 自定义显示页
// 具体看 package.json 中的 configuration 和 commands
import * as vscode from 'vscode'
import * as fs from 'fs'
import * as path from 'path'

module.exports = function (context: vscode.ExtensionContext) {
  let disposable = vscode.commands.registerCommand(
    'beehive.customWelcome',
    () => {
      const panel = vscode.window.createWebviewPanel(
        'welcome',
        '自定义欢迎页',
        vscode.ViewColumn.One,
        {
          enableScripts: true,
        }
      )

      const htmlPath = path.join(
        context.extensionPath,
        'src/customWelcome.html'
      )
      let html = fs.readFileSync(htmlPath, 'utf-8')
      panel.webview.html = html
    }
  )

  context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'

export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  require('./beehive-customWelcome')(context) // demo3 加载自定义WebView欢迎页
}

export function deactivate() {}
// package.json
"commands": [
    {
       "command": "beehive.customWelcome",
       "title": "demo4: beehive.customWelcome !"
    }
],
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>demo3 自定义欢迎页面</title>
        <style>
            .app {
                margin: 12px 0;
                font-size: 24px;
            }
            .title {
                font-size: 18px;
                margin-top: 24px;
            }
        </style>
    </head>
    <body>
        <div class="app">
            Welcome BeeHiver ~
            <div class="title">后人哀之而不鉴之 亦使后人而复哀后人也 !</div>
        </div>
        <img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" width="300" />
    </body>
</html>

效果展示

源码展示

05. 自定义菜单 Menu-例子

场景

适用于一些快捷按钮的自定义,可通过Menu操作

代码展示

// demo5 自定义菜单
import * as vscode from 'vscode'
module.exports = function (context: vscode.ExtensionContext) {
  let disposable = vscode.commands.registerCommand('beehive.customMenu', () => {
    vscode.window.showInformationMessage("I' am custom menu !")
  })
  context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  require('./beehive-customMenu')(context) // demo5 自定义菜单
}
export function deactivate() {}
// package.json
  "commands": [
      {
        "command": "beehive.customMenu",
        "title": "demo5: 启动自定义菜单"
      }
    ],
    "menus": {
      "editor/title": [
        {
          "command": "beehive.customMenu",
          "alt": "beehive.customMenu",
          "group": "navigation"
        }
      ]
    },

效果展示

点击之后,触发事件

源码展示

06. 悬停提示—例子

场景

当你鼠标光标hover至某个代码时,你想要显示一些文字内容

代码展示

// demo6 对package.json中的author进行悬停提示
import * as vscode from 'vscode'

module.exports = function (context: vscode.ExtensionContext) {
  let disposable = vscode.languages.registerHoverProvider('json', {
    provideHover(document, position, token) {
      const fileName = document.fileName
      const word = document.getText(document.getWordRangeAtPosition(position))
      if (/\/package\.json$/.test(fileName) && /\bauthor\b/.test(word)) {
        return new vscode.Hover('悬停提示: 彭道宽牛逼!')
      }
      return undefined
    },
  })

  context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  require('./beehive-hoverTips')(context) // demo6 悬停提示
}
export function deactivate() {}

效果展示

image

源码展示

08. 自定义侧边栏+面板——例子

背景

需要在左侧自定义侧边栏,完成一些交互逻辑操作

代码实现

⚠️ 需要注意:侧边栏按钮(Tree View Container)和面板视图(Tree View)要同时配置,否则不生效

侧边栏的展示

首先,我们先看官方文档,看看如何在左边这个侧边栏添加我们自定义的内容

👉 contribution-points#contributes.viewsContainers

// package.json
{
  "contributes": {
    "viewsContainers": {
      "activitybar": [
        {
          "id": "sugar",
          "title": "Sugar-BeeHive",
          "icon": "./src/logo/sugar.svg"
        }
      ]
    },
    "views": {
      "sugar": [
        {
          "id": "BeeHive-Command",
          "name": "01.命令集"
        },
        {
          "id": "BeeHive-PackageAnalysis",
          "name": "02.包分析"
        }
      ]
    },
  }
}

⚠️ 注意点:views 中 key 要和 activitybar 中的属性id保持一致,如 sugar 在两者中是一致的

这时候运行我们的插件:Run Extenstion,就可以看到在左侧有我们自定义的侧边栏啦

关于图标svg这个大家自己注意一下路径就好了,我这边重点不是图标哈~

上面我们配置完 package.json 之后,我们再回到文档,会看到这么一段话:tree-view#activationEvents

如果需要,你就加上下面这段代码即可

"activationEvents": [
  // 这个id是对应 contributes.views 中的 id
  "onView:BeeHive-Command",
 ],

如何定义面板内容

上面是展示出来了侧边栏,但是我们需要展示内容啊,怎么整?通过官方文档:tree-data-provider 可以实现一个小 demo

// beehive-sidebar.ts
// demo8 自定义侧边栏入口和面板
import * as vscode from 'vscode'

const scripts = [
  {
    script: 'webpack:dev',
  },
  {
    script: 'webpack:prod',
  },
  {
    script: 'server:dev',
  },
  {
    script: 'server:test',
  },
  {
    script: 'server:test-1',
  },
  {
    script: 'server:test-2',
  },
]

/**
 * @description 重写每个节点
 */
export class SideBarEntryItem extends vscode.TreeItem {
  constructor(private version: string, public readonly label: string, public readonly collapsibleState: vscode.TreeItemCollapsibleState
  ) {
    super(label, collapsibleState)
    this.tooltip = `${this.label}-${this.version}`
    this.description = `${this.version}-${Math.ceil(Math.random() * 1000)}`
  }
}

/**
 * @description 入口文件
 */
export class SideBarBeeHiveCommand implements vscode.TreeDataProvider<SideBarEntryItem> {
  constructor(private workspaceRoot?: string) {}
  getTreeItem(element: SideBarEntryItem): vscode.TreeItem {
    return element
  }

  getChildren(element?: SideBarEntryItem): vscode.ProviderResult<SideBarEntryItem[]> {
    if (element) {
      //子节点
      var childrenList = []
      for (let index = 0; index < scripts.length; index++) {
        var item = new SideBarEntryItem('1.0.0', scripts[index].script, vscode.TreeItemCollapsibleState.None)
        item.command = {
          command: 'BeeHive-Command.openChild', //命令id
          title: scripts[index].script,
          arguments: [scripts[index].script], //命令接收的参数
        }
        childrenList[index] = item
      }
      return childrenList
    } else {
      //根节点
      return [
        new SideBarEntryItem('1.0.0', '按钮组', vscode.TreeItemCollapsibleState.Collapsed),
      ]
    }
  }
}

export class SideBarBeeHivePackageAnalysis implements vscode.TreeDataProvider<SideBarEntryItem> {
  constructor(private workspaceRoot?: string) {}
  getTreeItem(element: SideBarEntryItem): vscode.TreeItem {
    return element
  }

  getChildren(element?: SideBarEntryItem): vscode.ProviderResult<SideBarEntryItem[]> {
    if (element) {
      //子节点
      var childrenList = []
      for (let index = 0; index < scripts.length; index++) {
        var item = new SideBarEntryItem('1.0.0', scripts[index].script, vscode.TreeItemCollapsibleState.None)
        item.command = {
          command: 'BeeHive-PackageAnalysis.openChild', //命令id
          title: scripts[index].script,
          arguments: [index], //命令接收的参数
        }
        childrenList[index] = item
      }
      return childrenList
    } else {
      //根节点
      return [
        new SideBarEntryItem('1.0.0', '按钮组', vscode.TreeItemCollapsibleState.Collapsed),
      ]
    }
  }
}

module.exports = function (context: vscode.ExtensionContext) {
  // 注册侧边栏面板
  const sidebarBeeHiveCommand = new SideBarBeeHiveCommand()
  const sidebarBeeHivePackageAnalysis = new SideBarBeeHivePackageAnalysis()
  vscode.window.registerTreeDataProvider(
    'BeeHive-Command',
    sidebarBeeHiveCommand
  )
  vscode.window.registerTreeDataProvider(
    'BeeHive-PackageAnalysis',
    sidebarBeeHivePackageAnalysis
  )

  //注册命令
  vscode.commands.registerCommand('BeeHive-Command.openChild', (args) => {
    console.log('[BeeHive-Command.openChild] 当前选中的是:', args)
    vscode.window.showInformationMessage(args)
  })
  vscode.commands.registerCommand(
    'BeeHive-PackageAnalysis.openChild',
    (args) => {
      console.log('[BeeHive-PackageAnalysis.openChild] 当前选中的是:', args)
      vscode.window.showInformationMessage(args)
    }
  )
}

然后在入口文件 extenstion.ts 添加该文件

import * as vscode from 'vscode'

export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  require('./beehive-sidebar')(context) // demo8 自定义侧边栏入口和面板
}

export function deactivate() {}

如果需要点击左侧侧边栏的节点时触发内容,只需要在 arguments 里面回传一些内容,然后做对应的业务操作即可

效果展示

image

空数据时的展示?

在我们的面板内容还没定义时,我们想要显示一些缺省内容提示?通过文档,我们可以知道 View Welcome content

"contributes": {
  "viewsWelcome": [
    {
      "view": "BeeHive-Command",
      "contents": "提高你的效率,释放你的双手~"
    }
  ]
}

image

源码阅读

02. 输入内容(Input)-例子

场景

该插件场景适合一些需要输入内容之后做的操作;

  • 输入 text ,发起接口请求等
  • 输入 text ,执行一些逻辑操作
  • ......

代码展示

// demo1 需要Input输入内容
import * as vscode from 'vscode'

module.exports = function (context: vscode.ExtensionContext) {
  let disposable = vscode.commands.registerCommand('beehive.inputName', () => {
    vscode.window
      .showInputBox({
        ignoreFocusOut: true,
        password: false,
        prompt: 'entry your name',
      })
      .then((value) => {
        if (value === undefined || value.trim() === '') {
          vscode.window.showInformationMessage('Please type your name.')
        } else {
          const name = value.trim()
          vscode.window.showInformationMessage('your name is: ', name)
          return
        }
      })
  })

  context.subscriptions.push(disposable)
}
// extension.ts
import * as vscode from 'vscode'

export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  require('./beehive-inputName')(context) // demo1 输入Input内容
}

export function deactivate() {}

效果展示

源码展示

09. 读取文件夹目录+内容复制剪切板——例子

场景

上一个例子: 自定义侧边栏+面板虽然可以实现我们的功能,但是还是有些缺点的,毕竟我们需要根据文件路径来动态生成我们的面板内容。

以下面例子为例:根据 vscode 工作目录,读取目录中 package.json 文件的 scripts 字段,筛选出符合规范的脚本命令,动态生成我们的按钮

首先需要明确的是:vscode有一个 vscode.workspace.rootPath,由于后来vscode支持multipleRoot模式,所以这个字段已经过时作废了。我们只能通过 vscode.workspace.workspaceFolders 获取当前工作区所有根文件夹数组

注意:是得到的跟文件夹数组路径,也就是说,下面这种情况,得到是路径内容是这样的

├── A_Folder
   ├── B_Folder
      ├── D_Folder 
      └──
     
   ├── C_Folder
   └──
└──

上面得到的只会是 : A_Folder 的路径,得不到下面的 B、C、D路径。

还需要注意的一点是:很难划分这个文件夹是不是属于一个前端工程或者Node工程,这边我是以该文件夹下有没有 package.json 来划分,也就是如果 A下面存在 package.json ,我就认为它是一个完整的工程项目(不把它当作文件夹)

真想得到 A 下面的所有文件夹?

如果你真的想得,我的想法是:只能通过 Node 的 fs 模块去获取 A 文件夹下的文件目录,然后递归,一路找下去,办法总会有的。

OK,扯远了,来看看大概的一个思路

代码展示

先注册一下侧边栏面板

// extension.ts
import * as vscode from 'vscode'

export function activate(context: vscode.ExtensionContext) {
  console.log('插件已启动,忙去吧~')
  require('./container/commandSet')(context)
}

export function deactivate() {}

不要忘记 package.json 也需要添加

{
  "contributes": {
      "commands": [],
      "viewsWelcome": [
        {
          "view": "BeeHive-Command",
          "contents": "提高你的效率,释放你的双手~"
        }
      ],
      "viewsContainers": {
        "activitybar": [
          {
            "id": "sugar",
            "title": "Sugar-BeeHive",
            "icon": "./assets/logo_default.svg"
          }
        ],
        "panel": [
          {
            "id": "sugar",
            "title": "Package Explorer",
            "icon": "./assets/logo_default.svg"
          }
        ]
      },
      "views": {
        "sugar": [
          {
            "id": "BeeHive-Command",
            "name": "01.命令集"
          },
          {
            "id": "BeeHive-Package",
            "name": "02.包分析"
          }
        ]
      }
    },
}

接下来就是我们的重头戏了,我们看看 require 进来的 commandSet 怎么写的~

// commandSet.ts
import * as vscode from 'vscode'
import SideBarCommand from './SideBarCommand'
import { PREFIX } from '../../constants'
import { ShellType } from '../../type/common'
import { getWorkSpaceFolderList } from '../../utils'

module.exports = function (context: vscode.ExtensionContext) {
  // 1. 得到vscode所有工程项目
  const folderList = getWorkSpaceFolderList()

  // 2. 注册侧边栏面板
  const sideBar = new SideBarCommand(folderList)
  vscode.window.registerTreeDataProvider('BeeHive-Command', sideBar)

  // 3. 注册命令
  vscode.commands.registerCommand(
    'BeeHive-Command.openChild',
    (args: { title: string; shell: ShellType; [key: string]: any }) => {
      const { title, shell = null, path = '' } = args
      const reg = new RegExp(`${PREFIX}`)
      if (reg.test(title)) {
        vscode.window.showInformationMessage(title)
      } else {
        // 4. 复制到剪切板
        vscode.env.clipboard.writeText(`cd ${path} \n npm run ${shell?.key}`)
        vscode.window.showInformationMessage(
          `ok, fine ! shell copied to clipboard ~`
        );
      }
    }
  )
}

接下来的重头戏就是,我们实现的这个 SideBarCommand 了,这里主要重写了 getChildren 方法,通过动态去生成面板内容

// SideBarCommand.ts
/**
 * @description 命令集侧边栏实例
 */
import * as vscode from 'vscode'
import { PREFIX } from '../../constants'
import { FolderType, ShellType } from '../../type/common'
import { isExist, read, getShellFromScripts } from '../../utils/package'
import { SideBarEntryItem, SideBarEntryListImplements,} from '../../factory/SideBar'

function getNode(
  title: string,
  description?: string,
  args?: { [key: string]: any }
) {
  let node = new SideBarEntryItem(title, vscode.TreeItemCollapsibleState.None, description)
  node.command = {
    command: 'BeeHive-Command.openChild', //命令id
    title: title,
    arguments: [{ title, ...args }], //命令接收的参数
  }
  return node
}

export default class SideBarCommand extends SideBarEntryListImplements {
  constructor(private folderPathList: FolderType[] | undefined) {
    super()
  }
  getChildren(
    element: SideBarEntryItem | undefined
  ): vscode.ProviderResult<SideBarEntryItem[]> {
    if (element) {
      var childrenList: any = []
      if (isExist(`${element.path}/package.json`)) {
        const packageValues = read(`${element.path}/package.json`)
        if (packageValues && packageValues.scripts) {
          const eggShell = getShellFromScripts(packageValues.scripts, 'server')
          const webpackShell = getShellFromScripts(packageValues.scripts, 'webpack')
          const shellList = [...webpackShell, ...eggShell]
          if (!!shellList.length) {
            shellList.forEach((shell: ShellType, index: number) => {
              const node = getNode(shell.key, `[${shell.environment}]`, { shell, path: element.path })
              childrenList[index] = node
            })
          } else {
            const noneNode = getNode(`[${PREFIX}]: scripts 脚本命令不符合规则`)
            childrenList = [noneNode]
          }
        } else {
          const noneNode = getNode(`[${PREFIX}]: 不存在 scripts 脚本命令`)
          childrenList = [noneNode]
        }
      } else {
        const noneNode = getNode(`[${PREFIX}]: 工程项目不存在package.json`)
        childrenList = [noneNode]
      }
      return childrenList
    } else {
      const folderNode = this.folderPathList?.map((folder: FolderType) => {
        return new SideBarEntryItem(
          folder.name,
          vscode.TreeItemCollapsibleState.Collapsed,
          '',
          folder.path
        )
      })
      return folderNode
    }
  }
}

上面的例子来自实战: vscode-beehive-extension

图片展示

具体关于业务代码,就不展示了~

源码阅读

07. 代码片段-例子

场景

输入一个前缀,会得到一个或多个提示,然后回车带出很多代码。

代码展示

需要修改 package.json 中的 snippets 的配置

// package.json
"snippets": [
  {
    "language": "html",
    "path": "./src/snippets/html.json"
  }
]

然后添加一个 html.json 配置

{
  "PDK": {
    "prefix": ["PDK", "PD", "PK", "DK"],
    "body": ["<PDK>", "${1}", "</PDK>"],
    "description": "彭道宽自定义的snippets"
  }
}

关于每个字段,可以通过官方文档了解:create-your-own-snippets
上面我们是设置语言为 : html,所以在运行插件,并保证插件被激活,在规定的语言html中,输入 prefix 相关的关键词,就可以啦

效果展示

源码展示

04. 快捷键注册-例子

场景

每次我们都需要 cmd+shift + P 调出选择器,然后输入我们注册的事件名,特别麻烦,vscode 支持快捷键注册,下面看看如何实现吧!

代码展示

   "commands": [
      {
        "command": "beehive.keybindings",
        "title": "demo4: beehive.keybindings !"
      }
    ],
    "keybindings": [
      {
        "command": "beehive.keybindings",
        "key": "Cmd+]",
        "mac": "Cmd+]",
        "when": "editorTextFocus"
      }
    ]

然后此时我们通过 Run Extension ,在本地窗口,我们随便打开一个文件,然后按下 : cmd + ] 就可以触发我们定义的
beehive.keybindings 事件

需注意,因为我们加了 when 属性,这边表示的是当编辑器聚焦时才可以

源码展示

01. 第一个vscode插件

一、环境准备

✅ nodejs
✅ npm
✅ yeoman
✅ generator-code

为了降低开发门槛,微软做了一个 Yeoman 代码生成命令,可以很方便的生成插件开发需要的模板代码,可以通过以下命令安装:

npm install -g yo generator-code

二、初始化 Demo 插件

当上面的安装完毕之后,只需要进入你开发目录文件夹,通过脚手架生成一个开发 vscode 插件的项目。

yo code

根据提示信息填写相关内容

image

这时候我们的 vscode 插件项目就 OK 了~ 接下来我们看看如何运行起来!

三、文件说明

项目中两个重要的文件我们需要看一下:extension.tspackage.json

3.1 extension.ts

该文件是入口文件,下面我们写一段简单的 demo 代码

此段代码意思为:注册一个 beehive.toastDemo 事件,当触发此事件会显示一段 message

import * as vscode from 'vscode'
export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active!')
  let disposable = vscode.commands.registerCommand('beehive.toastDemo', () => {
    vscode.window.showInformationMessage('toastDemo touched !')
  })
  context.subscriptions.push(disposable)
}
export function deactivate() {}

3.2 package.json

该文件配置项太多,建议去官方文档翻阅

关键是 : activationEventscontributes 这两个属性,关于 activationEvents 的意思和注意已经在上图说明

下面讲一下 contributes 相关信息,contributes 配置项是整个插件的贡献点,也就是说这个插件有哪些功能。contributes 字段可以设置的 key 也基本显示了 vscode 插件可以做什么。

  • configuration:通过这个配置项我们可以设置一个属性,这个属性可以在 vscode 的 settings.json 中设置,然后在插件工程中可以读取用户设置的这个值,进行相应的逻辑。
  • commands:命令,通过 cmd+shift+p 进行输入来实现的。
  • menus:通过这个选项我们可以设置右键的菜单
  • keybindings:可以设置快捷键
  • languages:设置语言特点,包括语言的后缀等
  • grammars:可以在这个配置项里设置描述语言的语法文件的路径,vscode 可以根据这个语法文件来自动实现语法高亮功能
  • snippets:设置语法片段相关的路径

四、运行 vscode 插件

点击此 Debug Icon,或者是 vscode 菜单栏:Run -> Start Debugging

当我们点击下 Run Extension 时,会开一个本地 vscode 窗口

我们在新开的 vscode 窗口中输入 : cmd + shift + P

然后输入我们注册的事件:beehive.toastDemo,然后我们按下回车,就会执行我们写的事件回调了!

至此,我们的第一个简单 Demo 完成!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.