first commit
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import * as vscode from "vscode";
|
||||
import sortImports from "import-sort";
|
||||
import sortStyle from "./sort-style";
|
||||
|
||||
// this method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
const channel = vscode.window.createOutputChannel("vsc-sort-import");
|
||||
const disposable = vscode.commands.registerCommand(
|
||||
"vsc-sort-import.sort",
|
||||
() => {
|
||||
try {
|
||||
const document = vscode.window.activeTextEditor?.document;
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
if (!/^(java|type)script(react)?$/.test(document.languageId)) {
|
||||
return;
|
||||
}
|
||||
const res = sortImports(
|
||||
document.getText(),
|
||||
"import-sort-parser-typescript",
|
||||
sortStyle
|
||||
);
|
||||
if (res.changes.length === 0) {
|
||||
// don't modify document if there is no change
|
||||
return;
|
||||
}
|
||||
vscode.window.activeTextEditor.edit((builder) => {
|
||||
const all = new vscode.Range(
|
||||
0,
|
||||
0,
|
||||
Number.MAX_SAFE_INTEGER,
|
||||
Number.MAX_SAFE_INTEGER
|
||||
);
|
||||
builder.replace(all, res.code);
|
||||
});
|
||||
} catch (e) {
|
||||
// if failed, print error and keep silent
|
||||
channel.appendLine(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
context.subscriptions.push(disposable);
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export function deactivate() {}
|
||||
@@ -0,0 +1,105 @@
|
||||
import {
|
||||
IComparatorFunction,
|
||||
ISorterFunction,
|
||||
IStyleAPI,
|
||||
IStyleItem,
|
||||
} from "import-sort-style";
|
||||
import { IImport } from "import-sort-parser";
|
||||
|
||||
export default function (styleApi: IStyleAPI): IStyleItem[] {
|
||||
const {
|
||||
and,
|
||||
always,
|
||||
dotSegmentCount,
|
||||
isAbsoluteModule,
|
||||
isNodeModule,
|
||||
isRelativeModule,
|
||||
name,
|
||||
startsWithAlphanumeric,
|
||||
unicode,
|
||||
} = styleApi;
|
||||
|
||||
// comparator function which place non-alphanumeric first
|
||||
const natural: IComparatorFunction = (a: string, b: string) => {
|
||||
if (a === b) {
|
||||
return 0;
|
||||
}
|
||||
const sa = startsWithAlphanumeric(a),
|
||||
sb = startsWithAlphanumeric(b);
|
||||
if (sa === sb) {
|
||||
return unicode(a, b);
|
||||
} else {
|
||||
return sa ? 1 : -1;
|
||||
}
|
||||
};
|
||||
// sort: use member primarily, if member is not avaliable, use module name
|
||||
const memberOrModule: (c: IComparatorFunction) => ISorterFunction = (
|
||||
comparator: IComparatorFunction
|
||||
) => {
|
||||
return (a: IImport, b: IImport): number => {
|
||||
const aHasDefaultMember = Boolean(a.defaultMember),
|
||||
aHasNamespaceMember = Boolean(a.namespaceMember),
|
||||
aHasNamedMember = Boolean(a.namedMembers[0]?.name),
|
||||
aHasOnlyDefaultMember =
|
||||
aHasDefaultMember && !aHasNamespaceMember && !aHasNamedMember,
|
||||
aHasOnlyNamespaceMember =
|
||||
aHasNamespaceMember && !aHasDefaultMember && !aHasNamedMember;
|
||||
const bHasDefaultMember = Boolean(b.defaultMember),
|
||||
bHasNamespaceMember = Boolean(b.namespaceMember),
|
||||
bHasNamedMember = Boolean(b.namedMembers[0]?.name),
|
||||
bHasOnlyDefaultMember =
|
||||
bHasDefaultMember && !bHasNamespaceMember && !bHasNamedMember,
|
||||
bHasOnlyNamespaceMember =
|
||||
bHasNamespaceMember && !bHasDefaultMember && !bHasNamedMember;
|
||||
if (aHasOnlyNamespaceMember !== bHasOnlyNamespaceMember) {
|
||||
return aHasOnlyNamespaceMember ? -1 : 1;
|
||||
} else if (aHasOnlyDefaultMember !== bHasOnlyDefaultMember) {
|
||||
return aHasOnlyDefaultMember ? -1 : 1;
|
||||
} else if (aHasNamespaceMember !== bHasNamespaceMember) {
|
||||
return aHasNamespaceMember ? -1 : 1;
|
||||
} else if (aHasDefaultMember !== bHasDefaultMember) {
|
||||
return aHasDefaultMember ? -1 : 1;
|
||||
} else if (aHasNamedMember !== bHasNamedMember) {
|
||||
return aHasNamedMember ? -1 : 1;
|
||||
}
|
||||
const first =
|
||||
a.defaultMember ||
|
||||
a.namespaceMember ||
|
||||
a.namedMembers[0]?.name ||
|
||||
a.moduleName;
|
||||
const second =
|
||||
b.defaultMember ||
|
||||
b.namespaceMember ||
|
||||
b.namedMembers[0]?.name ||
|
||||
b.moduleName;
|
||||
return comparator(first, second);
|
||||
};
|
||||
};
|
||||
|
||||
return [
|
||||
// built-in module, has member
|
||||
{
|
||||
match: isNodeModule,
|
||||
sort: memberOrModule(natural),
|
||||
sortNamedMembers: name(natural),
|
||||
},
|
||||
// absolute
|
||||
{
|
||||
match: isAbsoluteModule,
|
||||
sort: memberOrModule(natural),
|
||||
sortNamedMembers: name(natural),
|
||||
},
|
||||
// relative
|
||||
{
|
||||
match: isRelativeModule,
|
||||
sort: [dotSegmentCount, memberOrModule(natural)],
|
||||
sortNamedMembers: name(natural),
|
||||
},
|
||||
// fallback
|
||||
{
|
||||
match: always,
|
||||
sort: [dotSegmentCount, memberOrModule(natural)],
|
||||
sortNamedMembers: name(natural),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as path from 'path';
|
||||
|
||||
import { runTests } from 'vscode-test';
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// The folder containing the Extension Manifest package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
|
||||
|
||||
// The path to test runner
|
||||
// Passed to --extensionTestsPath
|
||||
const extensionTestsPath = path.resolve(__dirname, './suite/index');
|
||||
|
||||
// Download VS Code, unzip it and run the integration test
|
||||
await runTests({ extensionDevelopmentPath, extensionTestsPath });
|
||||
} catch (err) {
|
||||
console.error('Failed to run tests');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,15 @@
|
||||
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';
|
||||
|
||||
suite('Extension Test Suite', () => {
|
||||
vscode.window.showInformationMessage('Start all tests.');
|
||||
|
||||
test('Sample test', () => {
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import * as path from 'path';
|
||||
import * as Mocha from 'mocha';
|
||||
import * as glob from 'glob';
|
||||
|
||||
export function run(): Promise<void> {
|
||||
// Create the mocha test
|
||||
const mocha = new Mocha({
|
||||
ui: 'tdd',
|
||||
color: true
|
||||
});
|
||||
|
||||
const testsRoot = path.resolve(__dirname, '..');
|
||||
|
||||
return new Promise((c, e) => {
|
||||
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
|
||||
if (err) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
// Add files to the test suite
|
||||
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
|
||||
|
||||
try {
|
||||
// Run the mocha test
|
||||
mocha.run(failures => {
|
||||
if (failures > 0) {
|
||||
e(new Error(`${failures} tests failed.`));
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
e(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user