天问

VSCode源码分析

VSCode 作为一个代码编辑器,虽然不及 Idea,Eclipse 等老牌编辑器强大。但是远比 Sublime/Notepad++ 等所谓高手的编辑器强。

首先,上诉编辑器都提供插件方式对编辑器功能增强。

Idea,Eclipse: 功能强大,产品完善,但启动,运行消耗巨大,对电脑配置要求高。

VSCode :Bug多(github目前尚有近6000 issue未解决,本人就找到并提出近20个bug和功能改进),但代码提示,调试,版本管理等又有高级功能。所以比上不足,比下有余。

Sublime,Notepad++:这两个只能算是文本编辑器而已,“ 网传 ”高手IDE是这两货,这个“ 网传 ”多是一些培训机构忽悠学员来着。否则 Idea/MyEclipse 等收费软件就没市场了。企业开发,大型项目开发多是用如 Idea/MyEclipse 等工具开发,绝非 Sublime,Notepad++

---------------引

(1)环境准备

  • VS2015 tools
  • python2
  • nodejs+yarn

以上准备工作我早已经安装好,VS2015 tools这个直接安装 Virtual Studio 2017即可,自然会带命令行工具。python2我则早安装了Anaconda3,只需要创建一个虚拟的python2环境即可。nodejs下载windows版本安装即可。

(2)编译源码

clone源码,yarn下载依赖。

git clone https://github.com/Microsoft/vscode
git checkout master

C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio 2017\Visual Studio Tools\VC\x64 Native Tools Command Prompt for VS 2017.lnk

conda create -n py27 python=2.7 ipykernel numpy
activate py27
yarn
yarn watch

由于步骤比较死,但可能遇到很多问题。上述两个步骤就简单聊到这,更多可以取 github 上 wiki 上查看。

2.1 补充一:if not defined npm_config_node_gyp错误

有小伙伴执行出现上述错误,还没分析就和我联系报错,其实这个问题很简单,就是被墙了,connect ETIMEDOUT 3.219.64.173:443 这个IP就是上面的 https://atom.io/download/electron/v4.2.5/node-v4.2.5-headers.tar.gz链接无法下载。

解决办法只有一个,那就是“ 反墙 ”咯。。

set http_proxy=socks5://127.0.0.1:1080
set https_proxy=socks5://127.0.0.1:1080
set ftp_proxy=socks5://127.0.0.1:1080

2.2 The build tools for v140 (Platform Toolset = 'v140') cannot be found.

Building the projects in this solution one at a time. To enable parallel build, please add the "/m" switch.
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\VC\VCTargets\Microsoft.Cpp.Platform.targets(67,5): error MSB8020: The build tools for v140 (Platform Toolset = 'v140') cannot be found. To build using the v140 build tools, please install v140 build tools.  Alternatively, you may upgrade to the current Visual Studio tools by selecting the Project menu or right-click the solution, and then selecting "Retarget solution". [D:\liuyuqi\github\nodejs\vscode\node_modules\vscode-sqlite3\build\deps\action_before_build.vcxproj]
gyp ERR! build error
gyp ERR! stack Error: `msbuild` failed with exit code: 1
gyp ERR! stack     at ChildProcess.onExit (D:\Program-Files\nodejs\node_modules\npm\node_modules\node-gyp\lib\build.js:262:23)
gyp ERR! stack     at ChildProcess.emit (events.js:198:13)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:248:12)
gyp ERR! System Windows_NT 10.0.18362
gyp ERR! command "D:\\Program-Files\\nodejs\\node.exe" "D:\\Program-Files\\nodejs\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js" "rebuild"
gyp ERR! cwd D:\liuyuqi\github\nodejs\vscode\node_modules\vscode-sqlite3
                                                                                                                                                                        

vscode项目默认使用 visual studio 2015来编译的,而我系统安装的是 visual studio 2017,所以会提示找不到v140命令行工具。一种方法就是把环境变量:v141赋值给v140。

还有一种方法就是用virual studio重新打开项目,会提示升级项目。点击确定即可。用virtual studio打开:vscode\node_modules\vscode-sqlite3\build\deps\action_before_build.vcxproj 项目文件。

(3)源码分析

vscode 是微软开发的开源 IDE,适合前端开发。为啥这么说呢,因为这个IDE基于Nodejs开发,准确来说基于 Electron(electronjs)开发。开发语言为 TypeScript ,这是微软推出的面向对象的JavaScript编程语言,编写 TypeScript 代码后,可以编译生成 JavaScript 。所以就知道 JavaScript 不是一门严谨的语言了。。

3.1 项目结构

下面是代码结构图:

整个项目代码50M,这么少。。。src下项目源码,extensions系统插件源码,其他目录资源文件或者shell脚本。插件开发可以参考extensions中系统插件的写法。每次版本更新基本上围绕src、extensions目录。

3.2 yarn watch做了什么

既然是nodejs项目,必不可少 package.json 配置文件:

{
  "name": "code-oss-dev",
  "version": "1.37.0",
  "distro": "53215ea1e983c935e01e95875b9ee9ef65777c7d",
  "author": {
    "name": "Microsoft Corporation"
  },
  "license": "MIT",
  "main": "./out/main",
  "private": true,
  "scripts": {
    "test": "mocha",
    "preinstall": "node build/npm/preinstall.js",
    "postinstall": "node build/npm/postinstall.js",
    "compile": "gulp compile --max_old_space_size=4095",
    "watch": "gulp watch --max_old_space_size=4095",
    "watch-client": "gulp watch-client --max_old_space_size=4095",
  },
  "dependencies": {
    "applicationinsights": "1.0.8",
    "xterm": "3.15.0-beta71",
    "xterm-addon-search": "0.2.0-beta2",
    "xterm-addon-web-links": "0.1.0-beta10",
    "yauzl": "^2.9.2",
    "yazl": "^2.4.3"
  },
  "devDependencies": {
    "7zip": "0.0.6",
    "fancy-log": "^1.3.3",
    "fast-plist": "0.1.2",
    "glob": "^5.0.13",
    "gulp": "^4.0.0",
    "vinyl-fs": "^3.0.0",
    "vsce": "1.48.0",
    "vscode-debugprotocol": "1.35.0",
    "vscode-nls-dev": "^3.3.1",
    "webpack": "^4.16.5",
    "webpack-cli": "^3.1.0",
    "webpack-stream": "^5.1.1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/Microsoft/vscode.git"
  },
  "bugs": {
    "url": "https://github.com/Microsoft/vscode/issues"
  },
  "optionalDependencies": {
    "vscode-windows-ca-certs": "0.1.0",
    "vscode-windows-registry": "1.0.1",
    "windows-foreground-love": "0.2.0",
    "windows-mutex": "0.3.0",
    "windows-process-tree": "0.2.4"
  }
}
  • 代码入口:./out/main.js
  • 打包工具:gulp+webpack
  • "watch": "gulp watch --max_old_space_size=4095",

yarn watch命令会执行 gulp watch 命令,而这个命令在 gulpfile.js 文件中定义了:

3.3 自动检测文件编码分析

各编辑器多有自动检测编码的的功能,即不管用户GBK编码还是UTF8编码都能自动识别,并正确打开和保存:

但是,vscode却出现了BUG,文件utf8编码,却被识别为windows1252编码。而且这个bug两年都未能修复!那么现在看看这个bug吧,代码定位:

src\vs\base\node\encoding.ts

先来看看detectEncodingByBOMFromBuffer这个函数,这个是通过文件前几个字节检测编码的:

export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null, bytesRead: number): string | null {
	if (!buffer || bytesRead < UTF16be_BOM.length) {
		return null;
	}

	const b0 = buffer.readUInt8(0);
	const b1 = buffer.readUInt8(1);

	// UTF-16 BE
	if (b0 === UTF16be_BOM[0] && b1 === UTF16be_BOM[1]) {
		return UTF16be;
	}

	// UTF-16 LE
	if (b0 === UTF16le_BOM[0] && b1 === UTF16le_BOM[1]) {
		return UTF16le;
	}

	if (bytesRead < UTF8_BOM.length) {
		return null;
	}

	const b2 = buffer.readUInt8(2);

	// UTF-8
	if (b0 === UTF8_BOM[0] && b1 === UTF8_BOM[1] && b2 === UTF8_BOM[2]) {
		return UTF8;
	}

	return null;
}

步骤:

  • 读取文件头两个字节 :

const b0 = buffer.readUInt8(0);
const b1 = buffer.readUInt8(1);

  • utf16用2个字节标志,utf8 1-2个字节变长度表示:
export const UTF16be_BOM = [0xFE, 0xFF];
export const UTF16le_BOM = [0xFF, 0xFE];
export const UTF8_BOM = [0xEF, 0xBB, 0xBF];
  • 这样并不准确,utf8变长度就无法准确判别。此外gkb,big5等编码无法通过文件头2个字节就判断出来,所以就有开发者专门写了一个npm包通过“统计概率”尽可能检测代码编码。下面就是vscode中调用jschardet 做编码检测:
/**
 * Guesses the encoding from buffer.
 */
async function guessEncodingByBuffer(buffer: Buffer): Promise<string | null> {
	const jschardet = await import('jschardet');

	jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD;

	const guessed = jschardet.detect(buffer);
	if (!guessed || !guessed.encoding) {
		return null;
	}

	const enc = guessed.encoding.toLowerCase();

	// Ignore encodings that cannot guess correctly
	// (http://chardet.readthedocs.io/en/latest/supported-encodings.html)
	if (0 <= IGNORE_ENCODINGS.indexOf(enc)) {
		return null;
	}

	return toIconvLiteEncoding(guessed.encoding);
}

Guesses the encoding from buffer,很明显就是猜测文件编码的意思, 第一行导入了:jschardet 包,这个是一个第三方的文件编码检测npm包。然后调用detect方法返回编码:

const guessed = jschardet.detect(buffer);

调试一下发现,确实返回错误编码。那么就检查这个npm包问题咯:

这还是一个开源的npm项目:

https://www.npmjs.com/package/jschardet

github地址: https://github.com/aadsm/jschardet

目前该项目已经更新到2.1.0,而vscode却依然使用1.6.0版本。

博客地址:http://blog.yoqi.me/?p=16502
扫我捐助哦
喜欢 1

这篇文章还没有评论

发表评论