mirror of
https://github.com/microsoft/vscode-extension-samples.git
synced 2026-04-27 16:55:44 +08:00
add nodefs-provider-sample
This commit is contained in:
4
nodefs-provider-sample/.gitignore
vendored
Normal file
4
nodefs-provider-sample/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
out
|
||||
node_modules
|
||||
.vscode-test/
|
||||
*.vsix
|
||||
7
nodefs-provider-sample/.vscode/extensions.json
vendored
Normal file
7
nodefs-provider-sample/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"eg2.tslint"
|
||||
]
|
||||
}
|
||||
37
nodefs-provider-sample/.vscode/launch.json
vendored
Normal file
37
nodefs-provider-sample/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"${workspaceFolder}/test.code-workspace"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "npm: watch"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/test/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "npm: watch"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
nodefs-provider-sample/.vscode/settings.json
vendored
Normal file
9
nodefs-provider-sample/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
}
|
||||
}
|
||||
20
nodefs-provider-sample/.vscode/tasks.json
vendored
Normal file
20
nodefs-provider-sample/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
9
nodefs-provider-sample/.vscodeignore
Normal file
9
nodefs-provider-sample/.vscodeignore
Normal file
@ -0,0 +1,9 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
out/test/**
|
||||
out/**/*.map
|
||||
src/**
|
||||
.gitignore
|
||||
tsconfig.json
|
||||
vsc-extension-quickstart.md
|
||||
tslint.json
|
||||
3
nodefs-provider-sample/README.md
Normal file
3
nodefs-provider-sample/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# remote-fs
|
||||
|
||||
A sample extension that implements a file system provider for the scheme `datei://` using node.js FS APIs.
|
||||
38
nodefs-provider-sample/package.json
Normal file
38
nodefs-provider-sample/package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "nodefs-provider-sample",
|
||||
"displayName": "nodefs-provider-sample",
|
||||
"description": "",
|
||||
"version": "0.0.1",
|
||||
"publisher": "benjpas",
|
||||
"engines": {
|
||||
"vscode": "^1.22.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"*"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"postinstall": "node ./node_modules/vscode/bin/install",
|
||||
"test": "npm run compile && node ./node_modules/vscode/bin/test"
|
||||
},
|
||||
"dependencies": {
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "^2.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mkdirp": "^0.5.2",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/node": "^7.0.43",
|
||||
"@types/promisify-node": "^0.4.0",
|
||||
"@types/rimraf": "^2.0.2",
|
||||
"tslint": "^5.8.0",
|
||||
"typescript": "^2.6.1",
|
||||
"vscode": "^1.1.6"
|
||||
}
|
||||
}
|
||||
281
nodefs-provider-sample/src/extension.ts
Normal file
281
nodefs-provider-sample/src/extension.ts
Normal file
@ -0,0 +1,281 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
'use strict';
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as mkdirp from 'mkdirp';
|
||||
import * as rimraf from 'rimraf';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
vscode.workspace.registerFileSystemProvider('datei', new DateiFileSystemProvider(), {
|
||||
isCaseSensitive: process.platform === 'linux'
|
||||
});
|
||||
}
|
||||
|
||||
class DateiFileSystemProvider implements vscode.FileSystemProvider {
|
||||
|
||||
private _onDidChangeFile: vscode.EventEmitter<vscode.FileChangeEvent[]>;
|
||||
|
||||
constructor() {
|
||||
this._onDidChangeFile = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
|
||||
}
|
||||
|
||||
get onDidChangeFile(): vscode.Event<vscode.FileChangeEvent[]> {
|
||||
return this._onDidChangeFile.event;
|
||||
}
|
||||
|
||||
stat(uri: vscode.Uri, options: {}, token: vscode.CancellationToken): vscode.FileStat | Thenable<vscode.FileStat> {
|
||||
return this._stat(uri.fsPath);
|
||||
}
|
||||
|
||||
async _stat(path: string): Promise<vscode.FileStat> {
|
||||
return new FileStat(await _.stat(path));
|
||||
}
|
||||
|
||||
readFile(uri: vscode.Uri, options: vscode.FileOptions, token: vscode.CancellationToken): Uint8Array | Thenable<Uint8Array> {
|
||||
return _.readfile(uri.fsPath);
|
||||
}
|
||||
|
||||
readDirectory(uri: vscode.Uri, options: {}, token: vscode.CancellationToken): Thenable<[string, vscode.FileStat][]> {
|
||||
return this._readDirectory(uri, token);
|
||||
}
|
||||
|
||||
async _readDirectory(uri: vscode.Uri, token: vscode.CancellationToken): Promise<[string, vscode.FileStat][]> {
|
||||
const children = await _.readdir(uri.fsPath);
|
||||
|
||||
const stats: [string, vscode.FileStat][] = [];
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
_.checkCancellation(token);
|
||||
|
||||
const child = children[i];
|
||||
const stat = await this._stat(path.join(uri.fsPath, child));
|
||||
stats.push([child, stat]);
|
||||
}
|
||||
|
||||
return Promise.resolve(stats);
|
||||
}
|
||||
|
||||
createDirectory(uri: vscode.Uri, options: {}, token: vscode.CancellationToken): vscode.FileStat | Thenable<vscode.FileStat> {
|
||||
return this._createDirectory(uri, token);
|
||||
}
|
||||
|
||||
async _createDirectory(uri: vscode.Uri, token: vscode.CancellationToken): Promise<vscode.FileStat> {
|
||||
await _.mkdir(uri.fsPath); // TODO support cancellation
|
||||
|
||||
_.checkCancellation(token);
|
||||
|
||||
return this._stat(uri.fsPath);
|
||||
}
|
||||
|
||||
writeFile(uri: vscode.Uri, content: Uint8Array, options: vscode.FileOptions, token: vscode.CancellationToken): void | Thenable<void> {
|
||||
return this._writeFile(uri, content, options, token);
|
||||
}
|
||||
|
||||
async _writeFile(uri: vscode.Uri, content: Uint8Array, options: vscode.FileOptions, token: vscode.CancellationToken): Promise<void> {
|
||||
const exists = await _.exists(uri.fsPath);
|
||||
if (!exists) {
|
||||
_.checkCancellation(token);
|
||||
|
||||
if (!options.create) {
|
||||
throw vscode.FileSystemError.FileNotFound();
|
||||
}
|
||||
|
||||
await _.mkdir(path.dirname(uri.fsPath));
|
||||
} else {
|
||||
if (options.exclusive) {
|
||||
throw vscode.FileSystemError.FileExists();
|
||||
}
|
||||
}
|
||||
|
||||
_.checkCancellation(token);
|
||||
|
||||
return _.writefile(uri.fsPath, content as Buffer);
|
||||
}
|
||||
|
||||
delete(uri: vscode.Uri, options: {}, token: vscode.CancellationToken): void | Thenable<void> {
|
||||
return _.rmrf(uri.fsPath); // TODO support cancellation
|
||||
}
|
||||
|
||||
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: vscode.FileOptions, token: vscode.CancellationToken): vscode.FileStat | Thenable<vscode.FileStat> {
|
||||
return this._rename(oldUri, newUri, options, token);
|
||||
}
|
||||
|
||||
async _rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: vscode.FileOptions, token: vscode.CancellationToken): Promise<vscode.FileStat> {
|
||||
const exists = await _.exists(newUri.fsPath);
|
||||
if (exists) {
|
||||
if (options.exclusive) {
|
||||
throw vscode.FileSystemError.FileExists();
|
||||
} else {
|
||||
await _.rmrf(newUri.fsPath);
|
||||
}
|
||||
}
|
||||
|
||||
_.checkCancellation(token);
|
||||
|
||||
const parentExists = await _.exists(path.dirname(newUri.fsPath));
|
||||
if (!parentExists && !options.create) {
|
||||
throw vscode.FileSystemError.FileNotFound();
|
||||
}
|
||||
|
||||
_.checkCancellation(token);
|
||||
|
||||
if (!parentExists) {
|
||||
await _.mkdir(path.dirname(newUri.fsPath));
|
||||
}
|
||||
|
||||
_.checkCancellation(token);
|
||||
|
||||
await _.rename(oldUri.fsPath, newUri.fsPath);
|
||||
|
||||
_.checkCancellation(token);
|
||||
|
||||
return this._stat(newUri.fsPath);
|
||||
}
|
||||
|
||||
watch(uri: vscode.Uri, options: { recursive?: boolean | undefined; excludes?: string[] | undefined; }): vscode.Disposable {
|
||||
const watcher = fs.watch(uri.fsPath, { recursive: options.recursive }, async (event: string, filename: string | Buffer) => {
|
||||
const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString()));
|
||||
|
||||
// TODO support excludes (using minimatch library?)
|
||||
|
||||
this._onDidChangeFile.fire([{
|
||||
type: event === 'change' ? vscode.FileChangeType.Changed : await _.exists(filepath) ? vscode.FileChangeType.Created : vscode.FileChangeType.Deleted,
|
||||
uri: uri.with({ path: filepath })
|
||||
} as vscode.FileChangeEvent]);
|
||||
});
|
||||
|
||||
return { dispose: () => watcher.close() };
|
||||
}
|
||||
|
||||
// TODO can implement a fast copy() method with node.js 8.x new fs.copy method
|
||||
}
|
||||
|
||||
export function deactivate() { }
|
||||
|
||||
//#region Utilities
|
||||
|
||||
namespace _ {
|
||||
|
||||
function handleResult<T>(resolve: (result: T) => void, reject: (error: Error) => void, error: Error | null | undefined, result: T): void {
|
||||
if (error) {
|
||||
reject(massageError(error));
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
}
|
||||
|
||||
function massageError(error: Error & { code?: string }): Error {
|
||||
if (error.code === 'ENOENT') {
|
||||
return vscode.FileSystemError.FileNotFound();
|
||||
}
|
||||
|
||||
if (error.code === 'EISDIR') {
|
||||
return vscode.FileSystemError.FileIsADirectory();
|
||||
}
|
||||
|
||||
if (error.code === 'EEXIST') {
|
||||
return vscode.FileSystemError.FileExists();
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
export function checkCancellation(token: vscode.CancellationToken): void {
|
||||
if (token.isCancellationRequested) {
|
||||
throw new Error('Operation cancelled');
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeNFC(items: string): string;
|
||||
export function normalizeNFC(items: string[]): string[];
|
||||
export function normalizeNFC(items: string | string[]): string | string[] {
|
||||
if (process.platform !== 'darwin') {
|
||||
return items;
|
||||
}
|
||||
|
||||
if (Array.isArray(items)) {
|
||||
return items.map(item => item.normalize('NFC'));
|
||||
}
|
||||
|
||||
return items.normalize('NFC');
|
||||
}
|
||||
|
||||
export function readdir(path: string): Promise<string[]> {
|
||||
return new Promise<string[]>((resolve, reject) => {
|
||||
fs.readdir(path, (error, children) => handleResult(resolve, reject, error, normalizeNFC(children)));
|
||||
});
|
||||
}
|
||||
|
||||
export function stat(path: string): Promise<fs.Stats> {
|
||||
return new Promise<fs.Stats>((resolve, reject) => {
|
||||
fs.stat(path, (error, stat) => handleResult(resolve, reject, error, stat));
|
||||
});
|
||||
}
|
||||
|
||||
export function readfile(path: string): Promise<Buffer> {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
fs.readFile(path, (error, buffer) => handleResult(resolve, reject, error, buffer));
|
||||
});
|
||||
}
|
||||
|
||||
export function writefile(path: string, content: Buffer): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.writeFile(path, content, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
|
||||
export function exists(path: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
fs.exists(path, exists => handleResult(resolve, reject, null, exists));
|
||||
});
|
||||
}
|
||||
|
||||
export function rmrf(path: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
rimraf(path, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
|
||||
export function mkdir(path: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
mkdirp(path, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
|
||||
export function rename(oldPath: string, newPath: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.rename(oldPath, newPath, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FileStat implements vscode.FileStat {
|
||||
|
||||
constructor(private fsStat: fs.Stats) { }
|
||||
|
||||
get isFile(): boolean | undefined {
|
||||
return this.fsStat.isFile();
|
||||
}
|
||||
|
||||
get isDirectory(): boolean | undefined {
|
||||
return this.fsStat.isDirectory();
|
||||
}
|
||||
|
||||
get isSymbolicLink(): boolean | undefined {
|
||||
return this.fsStat.isSymbolicLink();
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this.fsStat.size;
|
||||
}
|
||||
|
||||
get mtime(): number {
|
||||
return this.fsStat.mtime.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
22
nodefs-provider-sample/src/test/extension.test.ts
Normal file
22
nodefs-provider-sample/src/test/extension.test.ts
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Note: This example test is leveraging the Mocha test framework.
|
||||
// Please refer to their documentation on https://mochajs.org/ for help.
|
||||
//
|
||||
|
||||
// The module 'assert' provides assertion methods from node
|
||||
import * as assert from 'assert';
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
// import * as vscode from 'vscode';
|
||||
// import * as myExtension from '../extension';
|
||||
|
||||
// Defines a Mocha test suite to group tests of similar kind together
|
||||
suite("Extension Tests", function () {
|
||||
|
||||
// Defines a Mocha unit test
|
||||
test("Something 1", function() {
|
||||
assert.equal(-1, [1, 2, 3].indexOf(5));
|
||||
assert.equal(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
22
nodefs-provider-sample/src/test/index.ts
Normal file
22
nodefs-provider-sample/src/test/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
|
||||
//
|
||||
// This file is providing the test runner to use when running extension tests.
|
||||
// By default the test runner in use is Mocha based.
|
||||
//
|
||||
// You can provide your own test runner if you want to override it by exporting
|
||||
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
|
||||
// host can call to run the tests. The test runner is expected to use console.log
|
||||
// to report the results back to the caller. When the tests are finished, return
|
||||
// a possible error to the callback or null if none.
|
||||
|
||||
import * as testRunner from 'vscode/lib/testrunner';
|
||||
|
||||
// You can directly control Mocha options by uncommenting the following lines
|
||||
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
|
||||
testRunner.configure({
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
||||
useColors: true // colored output from test results
|
||||
});
|
||||
|
||||
module.exports = testRunner;
|
||||
8
nodefs-provider-sample/test.code-workspace
Normal file
8
nodefs-provider-sample/test.code-workspace
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"uri": "datei:.",
|
||||
"name": "Test Folder"
|
||||
}
|
||||
]
|
||||
}
|
||||
23
nodefs-provider-sample/tsconfig.json
Normal file
23
nodefs-provider-sample/tsconfig.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
/* Strict Type-Checking Option */
|
||||
"strict": true, /* enable all strict type-checking options */
|
||||
/* Additional Checks */
|
||||
"noUnusedLocals": true /* Report errors on unused locals. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode-test"
|
||||
]
|
||||
}
|
||||
15
nodefs-provider-sample/tslint.json
Normal file
15
nodefs-provider-sample/tslint.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-string-throw": true,
|
||||
"no-unused-expression": true,
|
||||
"no-duplicate-variable": true,
|
||||
"curly": true,
|
||||
"class-name": true,
|
||||
"semicolon": [
|
||||
true,
|
||||
"always"
|
||||
],
|
||||
"triple-equals": true
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
}
|
||||
@ -62,6 +62,9 @@
|
||||
},
|
||||
{
|
||||
"path": "extension-deps-sample"
|
||||
},
|
||||
{
|
||||
"path": "nodefs-provider-sample"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user