Погружение в код: чтение исходного кода VSCode (1)

внешний интерфейс исходный код API Electron
Погружение в код: чтение исходного кода VSCode (1)

Об авторе Техническая команда zqlu Ant Financial Data Experience

VS Code — это новый инструмент, который сочетает в себе простоту редактора кода с инструментами, необходимыми разработчикам программ в процессе разработки, сборки и отладки. Code обеспечивает полную поддержку редактирования и отладки, расширяемую модель и упрощенную интеграцию с существующими инструментами.

ЭтоVSCode GithubСогласно введению на склад, сегодня количество звезд GitHub VSCode достигло 47 000. VSCode использует электрон, а использованный редактор кода называется Monaco. Monaco также является редактором кода, используемым сервисом Team Custome (Visual Studio Online ). Выше VSCode использует свой собственный разработку языка напечатает.

Прежде чем приступить к анализу собственного исходного кода VSCode, давайте сначала посмотрим на Electron, от которого зависит VSCode, и поймем, что Electron может лучше понять организацию кода и зависимости VSCode; во-вторых, режим внедрения зависимостей, используемый в исходном коде VSCode.

Electron

Electron — это фреймворк, который может использовать HTML, JavaScript и CSS для разработки десктопных приложений во фронтенде, материалов об Electron много. Мы можем взглянуть на пример приложения для быстрого старта, предоставленного официальным сайтом Electron:

вpackage.jsonОпределение выглядит следующим образом, обратите внимание наmainполя иstartсценарий: выполнитьnpm startТо есть запустить приложение Electron:

{
	"name": "electron-quick-start",
	"version": "1.0.0",
	"description": "A minimal Electron application",
	"main": "main.js",
	"scripts": {
		"start": "electron ."
	},
	"repository": "https://github.com/electron/electron-quick-start",
	"keywords": ["Electron", "quick", "start", "tutorial", "demo"],
	"author": "GitHub",
	"license": "CC0-1.0",
	"devDependencies": {
		"electron": "~1.7.8"
	}
}

тогда смотриmain.jsсценарий:

const electron = require('electron');
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;

const path = require('path');
const url = require('url');

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

function createWindow() {
	// Create the browser window.
	mainWindow = new BrowserWindow({ width: 800, height: 600 });

	// and load the index.html of the app.
	mainWindow.loadURL(
		url.format({
			pathname: path.join(__dirname, 'index.html'),
			protocol: 'file:',
			slashes: true,
		}),
	);

	// Open the DevTools.
	// mainWindow.webContents.openDevTools()

	// Emitted when the window is closed.
	mainWindow.on('closed', function() {
		// Dereference the window object, usually you would store windows
		// in an array if your app supports multi windows, this is the time
		// when you should delete the corresponding element.
		mainWindow = null;
	});
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed.
app.on('window-all-closed', function() {
	// On OS X it is common for applications and their menu bar
	// to stay active until the user quits explicitly with Cmd + Q
	if (process.platform !== 'darwin') {
		app.quit();
	}
});

app.on('activate', function() {
	// On OS X it's common to re-create a window in the app when the
	// dock icon is clicked and there are no other windows open.
	if (mainWindow === null) {
		createWindow();
	}
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

можно увидеть,mainСценарий в основном определяет функции обработки приложения для нескольких событий, среди которыхreadyВ обработчике событий создайтеBrowseWindowобъект и загрузитьindex.htmlстраница.

существуетindex.html, и снова загрузите его через тег скриптаrenderer.jsсценарий:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    <!-- All of the Node.js APIs are available in this renderer process. -->
    We are using Node.js <script>document.write(process.versions.node)</script>,
    Chromium <script>document.write(process.versions.chrome)</script>,
    and Electron <script>document.write(process.versions.electron)</script>.

    <script>
      // You can also require other files to run in this process
      require('./renderer.js')
    </script>
  </body>
</html>

На данный момент приложение экземпляра быстрого запуска Electron завершено, выполнитеnpm startПосле этого вы можете увидеть дисплей на интерфейсеindex.htmlв содержании.

Первое, что нам нужно понять, это два типа процессов, с которыми мы столкнемся в приложении Electron выше, и их различия, они называются основным процессом и процессом рендеринга.

Сначала взгляните на определения основного процесса и процесса рендеринга:

В электронном приложении,package.jsonсерединаmainПроцесс, в котором выполняется сценарий, называется основным процессом, а сценарий, работающий в основном процессе, отображает пользовательский интерфейс, создавая веб-страницы. Приложение Electron всегда имеет один и только один основной процесс. Поскольку Electron использует Chromium для отображения веб-страниц, в Chromium также используется многопроцессорная архитектура. Каждая веб-страница в Electron работает сама по себе.процесс рендерингасередина. В обычных браузерах веб-страницы обычно работают в изолированной среде, и им не разрешается касаться собственных ресурсов. Однако пользователи Electron могут выполнять некоторые низкоуровневые взаимодействия с операционной системой на странице с поддержкой Node.js API.

Разница между основным процессом и процессом рендеринга:

использование основного процессаBrowseWindowстраница создания экземпляра, каждыйBrowseWindowЭкземпляры запускают страницы в своем собственном процессе рендеринга. когдаBrowseWindowПосле уничтожения экземпляра соответствующий процесс рендеринга также прекращается. Основной процесс управляет всеми веб-страницами и соответствующими процессами рендеринга. Каждый процесс рендеринга независим, он заботится о веб-странице, на которой работает.

Для разработчиков важнее то, какие API могут использоваться скриптами в основном процессе и в процессе рендеринга.

Прежде всего, Electron API предоставляет множество API, некоторые из которых можно использовать только в основном процессе, некоторые API можно использовать только в процессе рендеринга, а некоторые можно использовать как в основном процессе, так и в процессе рендеринга.

Тогда для Node.js API, а также сторонних пакетов npm можно напрямую использовать как основной процесс, так и процесс рендеринга.

Наконец, поскольку процесс рендеринга выполняется на страницах хрома, все они также могут быть API-интерфейсами, предоставляемыми браузерами, такими как API-интерфейсы манипуляции с DOM.

API основной процесс процесс рендеринга
Electron API часть часть
Node.js API/module да да
API браузера нет да

Узнав об Electron, мы увидим, какой код в VSCode выполняется в основном процессе, а какой — в процессе рендеринга.

внедрение зависимости

Внедрение зависимостей, как шаблон проектирования, возможно, мало используется разработчиками интерфейса, но его можно увидеть повсюду в исходном коде VSCode, так что вот краткое введение. Сначала взгляните на определение внедрения зависимостей:

В программной инженерии внедрение зависимостей — это шаблон проектирования, который предоставляет объекты, от которых зависит класс объектов. Зависимые объекты называютсяService, инъекция относится к объекту, от которого будет зависетьServiceпередается объекту, потребляющему услугу (называемомуClient), чтобы покупательClientНет необходимости активно создавать (новые) зависимые сервисыService, и не нужно получать зависимые сервисы через заводской режимService.

В типичном шаблоне внедрения зависимостей существуют следующие категории ролей:

  • Объекты, от которых зависят и которые используются, т.е.Service
  • Клиентский объект, который использует службу, т.е.Client
  • Определение интерфейса для клиента, чтобы использовать службу,Interface
  • Инжектор: отвечает за создание сервисных объектов и предоставление их клиентам, обычно также отвечает за создание клиентских объектов.

Реализация внедрения зависимостей имеет несколько форм, одна из которых является распространенной — это внедрение зависимостей в стиле конструктора: Клиент объявляет службу, от которой он зависит, в параметрах своего конструктора, как показано в следующем коде TypeScript:

class Client {
	constructor(serviceA: ServiceA, serviceB: ServiceB) {
		// 注入器在建立Client的时候,将依赖的 Service 通过构造函数参数传递给 Client
		// Client此时即可将依赖的服务保存在自身状态内:
		this.serviceA = serviceA;
		this.serviceB = serviceB;
	}
}

Благодаря этому режиму Клиенту не нужно создавать требуемый объект Сервиса при его использовании.Одним из преимуществ этого является разделение построения и поведения объекта.После введения интерфейса зависимости между Клиентом и Сервисом нужен только интерфейс, чтобы определить, какие зависимые сервисные интерфейсы нужны Клиенту в параметрах конструктора, в сочетании с инжектором, могут дать клиентскому объекту больше гибкости и разъединения.

Наконец, в исходном коде VSCode большинство основных функций реализовано в виде сервисных объектов.Определение сервиса разделено на две части:

  • сервисный интерфейс
  • Идентификация сервисов: реализовано через декораторы на TypeScript

Когда Клиент объявляет зависимую Службу, он также объявляет ее в параметре конструктора Пример выглядит следующим образом:

class Client {
	constructor(
		@IModelService modelService: IModelService,
		@optional(IEditorService) editorService: IEditorService,
	) {
		// ...
		this.modelService = modelService;
		this.editorService = editorService;
	}
}

Здесь объявленный клиентский объектClient, зависит отServiceимеютIModelServiceа такжеIEditorService, где декоратор@IModelServiceэто идентификатор ModelService, за которым следуетIModelServiceПросто определение интерфейса в TypeScript;@optional(IEditorService)является идентификатором EditorService и передается черезoptionalДекоратор объявляется как необязательная зависимость.

Наконец, в коде, который фактически используетсяClientобъект, он должен быть предоставлен инжекторомinstantiationServiceЧтобы создать экземпляр Client:

const myClient = instantiationService.createInstance(Client);

организация исходного кода

После понимания Electron и внедрения зависимостей мы можем взглянуть на организацию исходного кода самого VSCode.

VSCode Core

Во-первых, ядро ​​его общего VSCodecoreи встроенные расширенияExtensionsсочинение,coreОн реализует базовый редактор кода и настольное приложение VSCode, а именно VSCode workbench; он также предоставляет API-интерфейсы расширений, позволяющие использовать встроенные расширения и расширения, разработанные сторонними компаниями, для расширения возможностей VSCode Core.

Первый взглядCoreорганизация исходного кода,CoreИсходный код разделен на следующие каталоги:

  • src/vs/base: определение основных служебных методов и основных элементов управления DOM UI.
  • src/vs/code: Редактор кода Monaco Editor: Он содержит отдельно упакованный редактор Monaco Editor и части, которые можно использовать только в VSCode.
  • src/vs/platform: Реализация внедрения зависимостей и основных сервисов, используемых VSCode.
  • src/vs/workbench: Внедрение VSCode Desktop Application Workbench.
  • src/vs/code: Запись приложения VSCode Electron, включая запись основного процесса Electron.

Во-вторых, поскольку VSCode опирается на Electron, мы упоминали выше, что у Electron есть основной процесс и процесс рендеринга, но API, которые они могут использовать, недоступны, поэтому VSCodeCoreОрганизация каждого каталога также организована в соответствии с API, которые они могут использовать. В каждом подкаталоге Core целевая среда, в которой выполняется код, делится на следующие категории:

  • common: используйте только исходный код JavaScript API, может работать в любой среде.
  • browser: исходный код, который должен использовать API, предоставляемый браузером, например манипулирование DOM и т. д.
  • node: нужно использоватьNode.jsИсходный код предоставленного API
  • electron-browser: Исходный код, необходимый для использования API рендерера Electron.
  • electron-main: Исходный код, необходимый для использования API основного процесса Electron.

Согласно приведенным выше правилам, а именноsrc/vs/workbench/browserИсходный код может использовать только базовый API JavaScript и API, предоставляемый браузером, в то время какsrc/vs/workbench/electron-browserИсходный код в браузере может использовать API JavaScript, API, предоставляемый браузером,Node.jsПредоставленный API и API в процессе рендеринга Electron.

VSCode Extensions

В репозитории кода VSCode указанный вышеsrc/vsизCoreКроме того, есть большой кусок встроенных расширений VSCode, их исходный код находится вextensionsВнутри.

Во-первых, VSCode используется как редактор кода, но время от времени расширяются различные функции редактирования кода, такие как подсветка синтаксиса, подсказки о завершении, проверка и т.д. Поэтому среди встроенных расширений VSCode большую часть составляют поддерживающие расширения различных языков программирования, такие как:extensions\html,extensions\javascript,extensions\cppИ так далее, большинство языковых расширений будут выглядеть как.tmTheme,.tmLanguageи т.д. Определение синтаксиса TextMate.

Другим типом встроенного расширения является расширение тела VSCode, такое как тело VSCode по умолчанию.extensions/theme-defaultsЖдать.

Ссылаться на


В этой статье кратко представлены некоторые приготовления, необходимые перед чтением исходного кода VSCode, в основном структура приложения Electron, шаблон проектирования внедрения зависимостей, а также исходный код VSCode и общая организация.

Следующее уведомление: ввод из командной строкиcodeКаков поток выполнения кода VSCode до появления настольного приложения VSCode?

Если вам интересна наша команда, вы можете подписаться на рубрику и подписатьсяgithubИли отправьте свое резюме на 'tao.qit####alibaba-inc.com'.replace('####', '@'), люди с высокими идеалами могут присоединиться~

Оригинальный адрес:GitHub.com/proto team/no…