vscode的代码工作区实现机制

news2024/11/25 22:40:26

工作区是编辑器的重要部分,它承载着编辑器和本地文件的连接,对文件增、删、改、查。下面我会介绍vscode工作区的创建。同样我们知道vscode软件打开的时候没有默认工作区,这里我对它进行了改造,软件启动时指向默认工作区。

工作区目录创建

我们先看下vscode在本地电脑的数据存储目录。
在这里插入图片描述
所有的缓存数据都在这里。我们也可以把项目中需要缓存的数据创建到这里面。
那么如何获取和设置软件数据存储目录呢?electron提供了官方api

  • app.getPath()
  • app.setPath()

官方文档地址: https://www.electronjs.org/zh/docs/latest/api/app#appgetpathname

在这里插入图片描述
对应setPath方法
在这里插入图片描述
我们看看vscode是如何创建软件数据存储目录的
src/main.js

const userDataPath = getUserDataPath(args);
app.setPath('userData', userDataPath);

跳进getUserDataPath方法

function factory(path, os, productName, cwd) {

		/**
		 * @param {NativeParsedArgs} cliArgs
		 *
		 * @returns {string}
		 */
		function getUserDataPath(cliArgs) {
			const userDataPath = doGetUserDataPath(cliArgs);
			const pathsToResolve = [userDataPath];
			if (!path.isAbsolute(userDataPath)) {
				pathsToResolve.unshift(cwd);
			}

			return path.resolve(...pathsToResolve);
		}

		/**
		 * @param {NativeParsedArgs} cliArgs
		 *
		 * @returns {string}
		 */
		function doGetUserDataPath(cliArgs) {

			// 1. Support portable mode
			const portablePath = process.env['VSCODE_PORTABLE'];
			if (portablePath) {
				return path.join(portablePath, 'user-data');
			}

			// 2. Support global VSCODE_APPDATA environment variable
			let appDataPath = process.env['VSCODE_APPDATA'];
			if (appDataPath) {
				return path.join(appDataPath, productName);
			}
			// 3. Support explicit --user-data-dir
			const cliPath = cliArgs['user-data-dir'];
			if (cliPath) {
				return cliPath;
			}

			// 4. Otherwise check per platform
			switch (process.platform) {
				case 'win32':
					appDataPath = process.env['APPDATA'];
					if (!appDataPath) {
						const userProfile = process.env['USERPROFILE'];
						if (typeof userProfile !== 'string') {
							throw new Error('Windows: Unexpected undefined %USERPROFILE% environment variable');
						}

						appDataPath = path.join(userProfile, 'AppData', 'Roaming');
					}
					break;
				case 'darwin':
					appDataPath = path.join(os.homedir(), 'Library', 'Application Support');
					break;
				case 'linux':
					appDataPath = process.env['XDG_CONFIG_HOME'] || path.join(os.homedir(), '.config');
					break;
				default:
					throw new Error('Platform not supported');
			}

			return path.join(appDataPath, productName);
		}

		return {
			getUserDataPath
		};
	}

其中os.homedir()方法是nodejs的os模块的内置应用程序编程接口,用于获取当前用户的主目录路径

到此软件缓存数据总目录创建完成。

下面我们在总目录下面创建一个workspace-content目录作为工作区目录来存储文件。

src/vs/platform/environment/common/environment.ts
在vscode环境service里面添加工作区名称

export interface IEnvironmentService {
...
    workspaceContent: URI;
   ... 
}    

实现接口
src/vs/platform/environment/common/environmentService.ts

export abstract class AbstractNativeEnvironmentService implements INativeEnvironmentService {
...
    @memoize
    get workspaceContent(): URI { return URI.file(join(this.userDataPath, 'workspace-content')); }
...
}

默认工作区目录创建
src/vs/code/electron-main/main.ts

Promise.all<string | undefined>([
    environmentMainService.extensionsPath,
    environmentMainService.codeCachePath,
    environmentMainService.logsPath,
    environmentMainService.globalStorageHome.fsPath,
    environmentMainService.workspaceStorageHome.fsPath,
    environmentMainService.localHistoryHome.fsPath,
    environmentMainService.backupHome,

    environmentMainService.workspaceTemplate.fsPath,
].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)),

在这里通过调用mkdir方法创建了各个数据存储目录。

我们知道vscode软件打开的时候没有默认工作区,这里我对它进行了改造,软件启动时指向默认工作区。

src/vs/platform/windows/electron-main/windowsMainService.ts

const workspaceContentUri = this.environmentService.workspaceContent;
const workspacePath: IPathToOpen[] = [
    {
        workspace: {
            id: '1',
            uri: workspaceContentUri
        },
        type: 2, // 2 File is a directory.
        exists: true
    }
];
// Identify things to open from open config

// const pathsToOpen = this.getPathsToOpen(openConfig); //愿逻辑
const pathsToOpen = workspacePath;
...

这里把加载工作区入口写死了

文件信息创建

vscode是如何读取文件工作区的uri创建工作区列表的呢?我们接着看
src/vs/workbench/services/configuration/browser/configurationService.ts

private async toValidWorkspaceFolders(workspaceFolders: WorkspaceFolder[]): Promise<WorkspaceFolder[]> {
		const validWorkspaceFolders: WorkspaceFolder[] = [];
		for (const workspaceFolder of workspaceFolders) {
			try {
				const result = await this.fileService.stat(workspaceFolder.uri);
				if (!result.isDirectory) {
					continue;
				}
			} catch (e) {
				this.logService.warn(`Ignoring the error while validating workspace folder ${workspaceFolder.uri.toString()} - ${toErrorMessage(e)}`);
			}
			validWorkspaceFolders.push(workspaceFolder);
		}
		return validWorkspaceFolders;
	}

这里读取了工作区的文件信息,这里的stat是对node fs模块通过stat获取文件信息进行了二次封装的,我们可以根据需求修改里面的数据。我们看下定义文件数据的地方

src/vs/platform/files/common/fileService.ts

async stat(resource: URI): Promise<IFileStatWithPartialMetadata> {
		const provider = await this.withProvider(resource);

		const stat = await provider.stat(resource);

		return this.toFileStat(provider, resource, stat, undefined, true, () => false /* Do not resolve any children */);
	}

在往里跟

private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat | { type: FileType } & Partial<IStat>, siblings: number | undefined, resolveMetadata: boolean, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStat> {
		const { providerExtUri } = this.getExtUri(provider);
		let isAudioFolderFlag = await this.exists(providerExtUri.joinPath(resource, 'main.audio'));
		// convert to file stat
		const fileStat: IFileStat = {
			resource,
			name: providerExtUri.basename(resource),
			isFile: (stat.type & FileType.File) !== 0,
			isDirectory: (stat.type & FileType.Directory) !== 0,
			isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0,
			mtime: stat.mtime,
			ctime: stat.ctime,
			size: stat.size,
			readonly: Boolean((stat.permissions ?? 0) & FilePermission.Readonly) || Boolean(provider.capabilities & FileSystemProviderCapabilities.Readonly),
			etag: etag({ mtime: stat.mtime, size: stat.size }),
			children: undefined,
			isAudioFolder: isAudioFolderFlag
		};
		// check to recurse for directories
		if (fileStat.isDirectory && recurse(fileStat, siblings)) {
			try {
				const entries = await provider.readdir(resource);
				// console.log('entries---', entries);
				const resolvedEntries = await Promises.settled(entries.map(async ([name, type]) => {
					try {
						const childResource = providerExtUri.joinPath(resource, name);
						// const childStat = resolveMetadata ? await provider.stat(childResource) : { type };
						/**
						 * @todo 获取子文件数据,愿逻辑由resolveMetadata开关控制
						 */
						const childStat = await provider.stat(childResource)
						return await this.toFileStat(provider, childResource, childStat, entries.length, resolveMetadata, recurse);
					} catch (error) {
						this.logService.trace(error);

						return null; // can happen e.g. due to permission errors
					}
				}));
				// make sure to get rid of null values that signal a failure to resolve a particular entry
				fileStat.children = coalesce(resolvedEntries);
			} catch (error) {
				this.logService.trace(error);

				fileStat.children = []; // gracefully handle errors, we may not have permissions to read
			}

			return fileStat;
		}

		return fileStat;
	}

可以看出这里生成文件信息
src/vs/workbench/contrib/files/common/explorerModel.ts
这个文件是Model层生成文件数据模型。后面用于渲染页面的。

static create(fileService: IFileService, configService: IConfigurationService, raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: readonly URI[]): ExplorerItem {

		let stat: ExplorerItem;
		if (raw.isAudioFolder) {
			stat = new ExplorerItem(raw.resource, fileService, configService, parent, false, raw.isSymbolicLink, raw.readonly, raw.name, raw.mtime, false, raw.ctime);
		} else {
			stat = new ExplorerItem(raw.resource, fileService, configService, parent, raw.isDirectory, raw.isSymbolicLink, raw.readonly, raw.name, raw.mtime, !raw.isFile && !raw.isDirectory, raw.ctime);
		}
		stat.isAudioFolder = (raw.isAudioFolder as boolean);
		if (stat.isDirectory &&!stat.isAudioFolder) {

			stat._isDirectoryResolved = !!raw.children || (!!resolveTo && resolveTo.some((r) => {
				return isEqualOrParent(r, stat.resource);
			}));

			if (raw.children) {
				for (let i = 0, len = raw.children.length; i < len; i++) {
					const child = ExplorerItem.create(fileService, configService, raw.children[i], stat, resolveTo);
					stat.addChild(child);
				}
			}
		}
		// 总 stat
		return stat;
	}

绘制文件列表

src/vs/workbench/contrib/files/browser/views/explorerView.ts

const promise = this.tree.setInput(input, viewState).then(async () => {
			
				...
		});

input是总工作区的文件信息
在这里插入图片描述

对文件排序功能

src/vs/workbench/contrib/files/common/files.ts
接口

export const enum SortOrder {
    Default = 'default',
    Mixed = 'mixed',
    FilesFirst = 'filesFirst',
    Type = 'type',
    ModifiedLower = 'modifiedLower',
    ModifiedUp = 'modifiedUp',
    NameLower = 'nameLower',
    NameUp = 'nameUp',
    createTimeLower = 'createTimeLower',
    createTimeUp = 'createTimeUp',
    FoldersNestsFiles = 'foldersNestsFiles',
}

settings文件里面的配置
src/vs/workbench/contrib/files/browser/files.contribution.ts

'explorer.sortOrder': {
    'type': 'string',
    'enum': [SortOrder.Default, SortOrder.Mixed, SortOrder.FilesFirst, SortOrder.Type, SortOrder.ModifiedLower, SortOrder.ModifiedUp, SortOrder.NameLower, SortOrder.NameUp, SortOrder.createTimeLower, SortOrder.createTimeUp, SortOrder.FoldersNestsFiles],
    'default': SortOrder.Default,
    'enumDescriptions': [
        nls.localize('sortOrder.default', 'Files and folders are sorted by their names. Folders are displayed before files.'),
        nls.localize('sortOrder.mixed', 'Files and folders are sorted by their names. Files are interwoven with folders.'),
        nls.localize('sortOrder.filesFirst', 'Files and folders are sorted by their names. Files are displayed before folders.'),
        nls.localize('sortOrder.type', 'Files and folders are grouped by extension type then sorted by their names. Folders are displayed before files.'),
        nls.localize('sortOrder.ModifiedLower', 'Files and folders are sorted by last modified date in descending order. Folders are displayed before  files.'),
        nls.localize('sortOrder.ModifiedUp', 'Files and folders are sorted by latest modified date in descending order. Folders are displayed before  files.'),
        nls.localize('sortOrder.nameLower', 'Files and folders are sorted by name'),
        nls.localize('sortOrder.nameUp', 'Files and folders are sorted by name'),
        nls.localize('sortOrder.createTimeLower', 'Files and folders are sorted by create time'),
        nls.localize('sortOrder.createTimeUp', 'Files and folders are sorted by create time'),
        nls.localize('sortOrder.foldersNestsFiles', 'Files and folders are sorted by their names. Folders are displayed before files. Files with nested children are displayed before other files.')
    ],
    'markdownDescription': nls.localize('sortOrder', "Controls the property-based sorting of files and folders in the explorer. When `#explorer.experimental.fileNesting.enabled#` is enabled, also controls sorting of nested files.")
},

实现

export class FileSorter implements ITreeSorter<ExplorerItem> {

	constructor(
		@IExplorerService private readonly explorerService: IExplorerService,
		@IWorkspaceContextService private readonly contextService: IWorkspaceContextService
	) { }

	compare(statA: ExplorerItem, statB: ExplorerItem): number {
		// Do not sort roots
		if (statA.isRoot) {
			if (statB.isRoot) {
				const workspaceA = this.contextService.getWorkspaceFolder(statA.resource);
				const workspaceB = this.contextService.getWorkspaceFolder(statB.resource);
				return workspaceA && workspaceB ? (workspaceA.index - workspaceB.index) : -1;
			}

			return -1;
		}

		if (statB.isRoot) {
			return 1;
		}

		const sortOrder = this.explorerService.sortOrderConfiguration.sortOrder;
		const lexicographicOptions = this.explorerService.sortOrderConfiguration.lexicographicOptions;

		let compareFileNames;
		let compareFileExtensions;
		switch (lexicographicOptions) {
			case 'upper':
				compareFileNames = compareFileNamesUpper;
				compareFileExtensions = compareFileExtensionsUpper;
				break;
			case 'lower':
				compareFileNames = compareFileNamesLower;
				compareFileExtensions = compareFileExtensionsLower;
				break;
			case 'unicode':
				compareFileNames = compareFileNamesUnicode;
				compareFileExtensions = compareFileExtensionsUnicode;
				break;
			default:
				// 'default'
				compareFileNames = compareFileNamesDefault;
				compareFileExtensions = compareFileExtensionsDefault;
		}

		// Sort Directories
		switch (sortOrder) {
			case 'type':
				if (statA.isDirectory && !statB.isDirectory) {
					return -1;
				}

				if (statB.isDirectory && !statA.isDirectory) {
					return 1;
				}

				if (statA.isDirectory && statB.isDirectory) {
					return compareFileNames(statA.name, statB.name);
				}

				break;

			case 'filesFirst':
				if (statA.isDirectory && !statB.isDirectory) {
					return 1;
				}

				if (statB.isDirectory && !statA.isDirectory) {
					return -1;
				}

				break;

			case 'foldersNestsFiles':
				if (statA.isDirectory && !statB.isDirectory) {
					return -1;
				}

				if (statB.isDirectory && !statA.isDirectory) {
					return 1;
				}

				if (statA.hasNests && !statB.hasNests) {
					return -1;
				}

				if (statB.hasNests && !statA.hasNests) {
					return 1;
				}

				break;

			case 'mixed':
				break; // not sorting when "mixed" is on

			default: /* 'default', 'modified' */
				if (statA.isDirectory && !statB.isDirectory) {
					return -1;
				}

				if (statB.isDirectory && !statA.isDirectory) {
					return 1;
				}

				break;
		}

		// Sort Files
		switch (sortOrder) {
			case 'createTimeLower':
				return (statA.mcime && statB.mcime && statA.mcime < statB.mcime) ? -1 : 1;
			case 'createTimeUp':
				return (statA.mcime && statB.mcime && statA.mcime < statB.mcime) ? 1 : -1;
			case 'nameUp':
				return statB.name.localeCompare(statA.name);
			case 'nameLower':
				return statA.name.localeCompare(statB.name);
			case 'type':
				return compareFileExtensions(statA.name, statB.name);

			case 'modifiedLower':
				if (statA.mtime !== statB.mtime) {
					return (statA.mtime && statB.mtime && statA.mtime < statB.mtime) ? 1 : -1;
				}

				return compareFileNames(statA.name, statB.name);

			case 'modifiedUp':
				if (statA.mtime !== statB.mtime) {
					return (statA.mtime && statB.mtime && statA.mtime < statB.mtime) ? -1 : 1;
				}

				return compareFileNames(statA.name, statB.name);

			default: /* 'default', 'mixed', 'filesFirst' */
				return compareFileNames(statA.name, statB.name);
		}
	}
}

注册菜单调用

// 按创建日期排序

// 一级菜单
const explorerSortByCreateTimeSubMenu = new MenuId('sortOrderByCreateTime');
MenuRegistry.appendMenuItem(MenuId.ViewTitle, <ISubmenuItem>{
    submenu: explorerSortByCreateTimeSubMenu,
    title: nls.localize('sortOrderByCreateTime', "按创建日期排序"),
    when: ContextKeyExpr.equals('view', VIEW_ID),
    group: 'group',
    order: 3,
});


// 二级菜单
registerAction2(class extends Action2 {
    constructor() {
        super({
            id: 'sortOrder.create.time.lower',
            title: nls.localize('sortOrder.create.time.lower', "最新至最旧"),
            menu: {
                id: explorerSortByCreateTimeSubMenu,
                order: 1,
            }
        });
    }

    async run(accessor: ServicesAccessor): Promise<void> {
        const config = accessor.get(IConfigurationService);
        const paneCompositeService = accessor.get(IPaneCompositePartService);
        const explorerService = accessor.get(IExplorerService);

        config.updateValue('explorer.sortOrder', SortOrder.createTimeLower);
        await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar);
        await explorerService.refresh();
    }
});

registerAction2(class extends Action2 {
    constructor() {
        super({
            id: 'sortOrder.create.time.up',
            title: nls.localize('sortOrder.create.time.up', "最旧至最新"),
            menu: {
                id: explorerSortByCreateTimeSubMenu,
                order: 2,
            }
        });
    }

    async run(accessor: ServicesAccessor): Promise<void> {
        const config = accessor.get(IConfigurationService);
        const paneCompositeService = accessor.get(IPaneCompositePartService);
        const explorerService = accessor.get(IExplorerService);

        config.updateValue('explorer.sortOrder', SortOrder.createTimeUp);
        await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar);
        await explorerService.refresh();
    }
});

registerAction2(class extends Action2 {
    constructor() {
        super({
            id: 'sortOrder.create.time.default',
            title: nls.localize('sortOrder.create.time.default', "默认排序"),
            menu: {
                id: explorerSortByCreateTimeSubMenu,
                order: 3,
            }
        });
    }

    async run(accessor: ServicesAccessor): Promise<void> {
        const config = accessor.get(IConfigurationService);
        const paneCompositeService = accessor.get(IPaneCompositePartService);
        const explorerService = accessor.get(IExplorerService);

        config.updateValue('explorer.sortOrder', SortOrder.Default);
        await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar);
        await explorerService.refresh();
    }
});

vscode网页版工作区创建机制

https://insiders.vscode.dev/
网页端使用的是window.showOpenFilePicker 浏览器API.
官方文档地址:https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API

src/vs/workbench/services/dialogs/browser/fileDialogService.ts

...
try {
	([fileHandle] = await window.showOpenFilePicker({ multiple: false }));
} catch (error) {
	return; // `showOpenFilePicker` will throw an error when the user cancels
}
...

其余流程一样

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/722735.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用PLSQL导入excel数据

1、写查询语句&#xff0c;查询需要修改的表&#xff0c;并在语句后加for update。 select * from bn_inf_apply for update 2、打开excel&#xff0c;整理需要导入的数据&#xff0c;需要注意两点&#xff1a; 1、将标题行删除。 2、在首列左侧增加一个空白行 3、 选中exce…

React02-JSX 语法

一、React 基础知识 1. React.createElement React.createElement 是 jsx 语法的编译结果&#xff0c;这个函数用来创建一个 React 元素&#xff0c;它的返回值是一个 Virtual DOM&#xff08;虚拟DOM&#xff09;。 React.createElement(type, props, ...children) 第一个…

数据库之MySQL仓库安装

目录 数据库之MySQL仓库安装 在官方文档获得安装地址 点击链接进入后&#xff0c;选择 MySQL Yum Repository 进入网页后&#xff0c;选择自己Linux所对应的版本&#xff08;我的是Linux8&#xff09; 选择No thanks&#xff0c;juststart my download 选择复制链接地址 …

12.1 计算机硬件基础

目录 底层课程导学 课程回顾 1.编程基础 2.应用开发&#xff08;使用操作系统提供的函数开发应用程序&#xff09; 3.底层开发&#xff08;开发操作系统本身&#xff0c;向下管理硬件、向上提供接口&#xff08;API&#xff09;&#xff09; 嵌入式系统分层 应用开发 底…

PLC技能引领自动化控制系统的设计与开发

学习了PLC后&#xff0c;您可以从事各种与自动化控制系统相关的工作。以下是一些可能的职业方向&#xff1a; 自动化工程师&#xff1a;作为自动化工程师&#xff0c;您将负责设计、开发和维护自动化控制系统&#xff0c;其中包括PLC编程、传感器和执行器的集成以及系统调试和…

CAD怎么转JPG图片?三种方法教你快速转换

CAD文件只能在安装了CAD软件的电脑上打开&#xff0c;而JPG图片可以在几乎所有设备上打开&#xff0c;包括电脑、手机和平板电脑等&#xff0c;这样可以更方便地分享和传递设计文件。无需考虑对方是否有安装CAD软件。那么我们怎么把CAD文件转换成JPG图片呢&#xff1f;教大家三…

管理类联考——英语——趣味篇——不择手段——c开头单词

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&…

CMIS_GOV_STD

自然资源“一张图”综合管理系统【标准版】

shell脚本-cp命令复制目录报错cp: omitting directory

cp 复制目录报错&#xff0c;如下&#xff1a; 【报错原因】&#xff1a; cp命令默认是不能复制目录的&#xff0c;需要加参数 【解决办法】&#xff1a; 使用cp -r命令进行复制&#xff0c;递归处理&#xff0c;将指定目录下的所有文件与子目录一并处理。 【拓展】cp语法 cp…

数据标注:数字病理学中的人工智能

在数字病理学中&#xff0c;组织样本的显微图像被传输到计算机&#xff0c;并使用先进的图像处理技术和计算机视觉对其进行分析。 医学图像和诊断的数字化为病理学中的人工智能开辟了途径。病理学家可以使用机器学习模型来进行增强分析并提高结果准确性。此外&#xff0c;病理学…

socat: 网络链接和一些简易的RPC

除了这些功能之外&#xff0c;端口转发&#xff0c;也是相当的方便实用, 小型产品的RPC 都可以通过&#xff0c;指定程序去执行

华为OD机试真题B卷 Python 实现【整理扑克牌】,附详细解题思路

目录 一、题目描述步骤1步骤2步骤3 二、输入描述三、输出描述四、解题思路五、Python算法源码六、效果展示1、输入2、输出3、说明 一、题目描述 给定一组数字&#xff0c;表示扑克牌的牌面数字&#xff0c;忽略扑克牌的花色&#xff0c;请按如下规则对这一组扑克牌进行整理&am…

使用kubeshpere创建k8s容器日志系统grafana-loki

k8s日志聚合平台grafana-loki&#xff0c;可以统一查看所有容器的日志运行。 效果&#xff1a; 使用kubeshpere创建loki应用十分的方便&#xff0c;减少了很多操作&#xff0c;易用的同时也实用&#xff0c;k8s不再是精通人员才能使用的编排系统。 1、在企业空间中&#xff0…

100种思维模型之能力圈思维模型-91

芒格说&#xff1a; “ 一个人在一生中可以真正得到的真见卓识仍然非常有限&#xff0c;所以正确的决策必须局限在自己的 ‘ 能力圈’ 以内。 ” 巴菲特说&#xff1a; “对你的 能力圈 来说&#xff0c;最重要的不是能力圈的范围大小&#xff0c;而是你如何能够 确定能…

虹科教程 | Linux网络命名空间与虹科PROFINET协议栈的GOAL中间件结合使用

前言 PROFINET是由PI推出的开放式工业以太网标准&#xff0c;它使用TCP/IP等IT标准&#xff0c;并由IEC 61158和IEC 61784 标准化&#xff0c;具有实时功能&#xff0c;并能够无缝集成到现场总线系统中。凭借其技术的开放性、灵活性和性能优势&#xff0c;PROFINET可应用于过程…

git上传文件到Gitee报错“error: failed to push some refs to ‘https://gitee.com/xxxx”

文章目录 前言一、创建项目仓库二、创建工作区三、配置 LFS四、上传镜像文件 前言 我要将一个 4.27 GB 的文件上传到 Gitee 上&#xff0c;但是出现了下面这样的报错 error: failed to push some refs to https://gitee.com/xxxx/centos.git 因此记录一下解决报错的方法。 一、…

eBPF内核技术在滴滴云原生的落地实践

将滴滴技术设为“星标⭐️” 第一时间收到文章更新 导读 eBPF是Linux内核革命性技术&#xff0c;能够安全高效地扩展内核能力&#xff0c;应用广泛&#xff0c;尤其是在云原生可观测性领域的应用已经成为行业热点。在滴滴云原生环境中&#xff0c;eBPF技术进行了业务实践和内源…

23西安电子科技大学电子工程学院821考研录取情况

01、电子工程学院各个方向 02、23电子工程学院一志愿考研录取情况总览、平均分 PS&#xff1a;电院23年院线相对于22年院线上涨10-15分&#xff0c;个别专业下降5分&#xff0c;这是因为22年西电电院报名人数较少&#xff0c;导致23年增加了一部分人数。 PS&#xff1a;1、电院…

前端 | windows安装nvm管理node.js

文章目录 01 nvm介绍02 安装03 设置镜像04 安装与版本管理05 基础使用 01 nvm介绍 nvm&#xff0c;即node version manager&#xff0c;是一个方便切换和管理node.js版本的工具。 安装nvm前的注意事项&#xff1a; 卸载掉现有的node.js所有安装目录避免出现空格和中文选用管理…

PaoPao泡泡清新文艺的微社区系统源码

泡泡是一个真正意义上高性能的微社区&#xff0c;虽然规模不大却拥有完备的功能。 该系统采用了GoZinc架构&#xff0c;前端使用Vue3进行构建&#xff0c;采用清新简洁的设计风格&#xff0c;布局类似Twitter的三栏设计。 后端服务使用Go编写&#xff0c;仅占用8MB的内存&…