jupyterlab_vscode_icons_extension 1.2.2 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/lib/__tests__/activation.spec.js +25 -1
- package/lib/__tests__/artwork-geometry.spec.js +4 -3
- package/lib/__tests__/manifest-validation.spec.d.ts +10 -0
- package/lib/__tests__/manifest-validation.spec.js +92 -0
- package/lib/__tests__/settings-alert.spec.js +3 -1
- package/lib/__tests__/settings-schema.spec.d.ts +1 -2
- package/lib/__tests__/settings-schema.spec.js +1 -2
- package/lib/index.d.ts +1 -0
- package/lib/index.js +349 -152
- package/package.json +4 -1
- package/src/__tests__/activation.spec.ts +37 -1
- package/src/__tests__/artwork-geometry.spec.ts +4 -3
- package/src/__tests__/manifest-validation.spec.ts +109 -0
- package/src/__tests__/settings-alert.spec.ts +3 -1
- package/src/__tests__/settings-schema.spec.ts +1 -2
- package/src/index.ts +407 -178
- package/style/base.css +18 -0
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ This extension brings 1414 beautiful file type icons from the vscode-icons proje
|
|
|
25
25
|
- Lightweight integration using Iconify's JSON icon format
|
|
26
26
|
- Compatible with Jupytext - properly displays Python and Markdown icons for .py and .md notebook files; includes hotfix for jupytext 1.19.1 catch-all pattern bug that breaks standard file icons
|
|
27
27
|
- Python package folder detection - folders declared in `pyproject.toml` or `setup.py` get a special Python package icon
|
|
28
|
+
- Symbolic link badges - a link to a file or folder carries a small square arrow badge on its row icon
|
|
29
|
+
- Executable file badges - an optional group, off by default, that marks files with the execute bit
|
|
28
30
|
- Document tab icons matching the file browser - an open file carries the same glyph on its tab as on its row, and an icon another extension registered is kept rather than replaced
|
|
29
31
|
- Readable on both themes - a glyph that cannot be seen against the panel it sits on is given a thin outline, decided per theme and re-decided when the theme changes
|
|
30
32
|
|
|
@@ -68,6 +70,7 @@ Customize which icon groups to display via JupyterLab Settings:
|
|
|
68
70
|
- Configuration File Icons (Dockerfile, .gitignore, Makefile, package.json)
|
|
69
71
|
- Documentation Icons (Markdown, RST, PDF)
|
|
70
72
|
- Image File Icons (PNG, JPG, SVG)
|
|
73
|
+
- Executable File Icons (play badge on files with the execute bit; off by default)
|
|
71
74
|
|
|
72
75
|
Changes require a page refresh to take effect.
|
|
73
76
|
|
|
@@ -46,7 +46,9 @@ const fakeApp = () => {
|
|
|
46
46
|
return { dispose: () => undefined };
|
|
47
47
|
},
|
|
48
48
|
getFileTypesForPath: () => [],
|
|
49
|
-
getFileType: () => undefined
|
|
49
|
+
getFileType: () => undefined,
|
|
50
|
+
getFileTypeForModel: () => ({ name: 'text' }),
|
|
51
|
+
fileTypes: () => []
|
|
50
52
|
};
|
|
51
53
|
return {
|
|
52
54
|
app: {
|
|
@@ -72,6 +74,28 @@ describe('activate without the optional services', () => {
|
|
|
72
74
|
expect(registry.types.length).toBeGreaterThan(0);
|
|
73
75
|
expect(registry.types.map(t => t.name)).toContain('vscode-file-type-js-official');
|
|
74
76
|
});
|
|
77
|
+
// DEF-ICONS-49: one pattern covered CHANGELOG and CHANGELOG.md, so the
|
|
78
|
+
// markdown file was typed text/plain and drew the text-page glyph.
|
|
79
|
+
it('types CHANGELOG.md as markdown and the bare CHANGELOG as text', () => {
|
|
80
|
+
const { app, registry } = fakeApp();
|
|
81
|
+
plugin.activate(app, null, null);
|
|
82
|
+
const types = registry.types;
|
|
83
|
+
const claiming = (file) => types.filter(t => t.pattern && new RegExp(t.pattern).test(file));
|
|
84
|
+
const md = claiming('CHANGELOG.md');
|
|
85
|
+
expect(md.map(t => t.name)).toEqual(['vscode-changelog-md']);
|
|
86
|
+
expect(md[0].mimeTypes).toEqual(['text/markdown']);
|
|
87
|
+
expect(md[0].icon.name).toBe('ui-components:markdown');
|
|
88
|
+
const bare = claiming('CHANGELOG');
|
|
89
|
+
expect(bare.map(t => t.name)).toEqual(['vscode-changelog']);
|
|
90
|
+
expect(bare[0].mimeTypes).toEqual(['text/plain']);
|
|
91
|
+
});
|
|
92
|
+
// ACC-ICONS-36: an Arduino sketch is C++ source.
|
|
93
|
+
it('maps .ino to the C++ file type', () => {
|
|
94
|
+
const { app, registry } = fakeApp();
|
|
95
|
+
plugin.activate(app, null, null);
|
|
96
|
+
const cpp = registry.types.find(t => t.name === 'vscode-file-type-cpp');
|
|
97
|
+
expect(cpp.extensions).toContain('.ino');
|
|
98
|
+
});
|
|
75
99
|
it('declares both services optional, which is what makes null legal', () => {
|
|
76
100
|
var _a;
|
|
77
101
|
expect(plugin.optional).toHaveLength(2);
|
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
*/
|
|
10
10
|
const source = require('fs').readFileSync(require('path').join(__dirname, '..', 'index.ts'), 'utf8');
|
|
11
11
|
describe('the markdown M', () => {
|
|
12
|
-
// Two
|
|
12
|
+
// Two earlier ten percent reductions, then twenty percent of the remaining
|
|
13
|
+
// height: 1 -> 0.9 -> 0.81 -> 0.648.
|
|
13
14
|
// The transform scales about y=0, so a translate of baseline * (1 - scale)
|
|
14
15
|
// puts the baseline back where it was and the letter loses height from the
|
|
15
16
|
// top only. The M's baseline is the lowest coordinate in its own path data.
|
|
16
17
|
const BASELINE = 189.5437;
|
|
17
|
-
const SCALE = 0.
|
|
18
|
+
const SCALE = 0.648;
|
|
18
19
|
const transform = () => {
|
|
19
20
|
const match = source.match(/<path fill="#8a2ea5" transform="translate\(0,([0-9.]+)\) scale\(1,([0-9.]+)\)"/);
|
|
20
21
|
if (!match) {
|
|
@@ -22,7 +23,7 @@ describe('the markdown M', () => {
|
|
|
22
23
|
}
|
|
23
24
|
return { translate: Number(match[1]), scale: Number(match[2]) };
|
|
24
25
|
};
|
|
25
|
-
it('is
|
|
26
|
+
it('is twenty percent shorter than the previous 81 percent height', () => {
|
|
26
27
|
expect(transform().scale).toBeCloseTo(SCALE, 6);
|
|
27
28
|
});
|
|
28
29
|
it('keeps its baseline where the unscaled letter put it', () => {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest validation before package name extraction - DEF-ICONS-55.
|
|
3
|
+
*
|
|
4
|
+
* ACC-PYPKG-17 promises that a malformed pyproject.toml or setup.py leaves
|
|
5
|
+
* every folder with its normal icon. The local parsers are regexes and still
|
|
6
|
+
* take a name out of a manifest that does not parse, so the source goes to
|
|
7
|
+
* the server's validation route first and an invalid answer drops the names.
|
|
8
|
+
* A failed call - the frontend-only install has no route - keeps them.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest validation before package name extraction - DEF-ICONS-55.
|
|
3
|
+
*
|
|
4
|
+
* ACC-PYPKG-17 promises that a malformed pyproject.toml or setup.py leaves
|
|
5
|
+
* every folder with its normal icon. The local parsers are regexes and still
|
|
6
|
+
* take a name out of a manifest that does not parse, so the source goes to
|
|
7
|
+
* the server's validation route first and an invalid answer drops the names.
|
|
8
|
+
* A failed call - the frontend-only install has no route - keeps them.
|
|
9
|
+
*/
|
|
10
|
+
jest.mock('@jupyterlab/settingregistry', () => ({
|
|
11
|
+
ISettingRegistry: { name: 'ISettingRegistry' }
|
|
12
|
+
}));
|
|
13
|
+
jest.mock('@jupyterlab/filebrowser', () => ({
|
|
14
|
+
IDefaultFileBrowser: { name: 'IDefaultFileBrowser' }
|
|
15
|
+
}));
|
|
16
|
+
jest.mock('@jupyterlab/services', () => ({
|
|
17
|
+
ServerConnection: {
|
|
18
|
+
makeSettings: () => ({ baseUrl: '/' }),
|
|
19
|
+
makeRequest: jest.fn()
|
|
20
|
+
}
|
|
21
|
+
}));
|
|
22
|
+
jest.mock('@jupyterlab/ui-components', () => ({
|
|
23
|
+
LabIcon: class {
|
|
24
|
+
constructor(options) {
|
|
25
|
+
this.name = options.name;
|
|
26
|
+
this.svgstr = options.svgstr;
|
|
27
|
+
}
|
|
28
|
+
element() {
|
|
29
|
+
return document.createElement('div');
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
fileIcon: { name: 'ui-components:file' },
|
|
33
|
+
markdownIcon: { name: 'ui-components:markdown' }
|
|
34
|
+
}));
|
|
35
|
+
import { ServerConnection } from '@jupyterlab/services';
|
|
36
|
+
import { packageNamesFromManifest } from '../index';
|
|
37
|
+
import { parsePyprojectToml, parseSetupPy } from '../parsers';
|
|
38
|
+
const makeRequest = ServerConnection.makeRequest;
|
|
39
|
+
const answer = (valid) => {
|
|
40
|
+
makeRequest.mockResolvedValue({
|
|
41
|
+
ok: true,
|
|
42
|
+
json: async () => ({ valid })
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
describe('packageNamesFromManifest', () => {
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
makeRequest.mockReset();
|
|
48
|
+
});
|
|
49
|
+
it('adds no name a malformed pyproject.toml would otherwise contribute', async () => {
|
|
50
|
+
// The name line is complete; the unterminated string after it breaks the
|
|
51
|
+
// document, and the local regex takes the name anyway.
|
|
52
|
+
const source = '[project]\nname = "sample"\ninvalid = [\n';
|
|
53
|
+
expect(parsePyprojectToml(source).size).toBeGreaterThan(0);
|
|
54
|
+
answer(false);
|
|
55
|
+
expect(await packageNamesFromManifest(source, 'toml')).toEqual(new Set());
|
|
56
|
+
});
|
|
57
|
+
it('adds no name a truncated setup.py would otherwise contribute', async () => {
|
|
58
|
+
// The closure of setup() is missing, so the file is not valid Python; the
|
|
59
|
+
// local regex still finds the name.
|
|
60
|
+
const source = 'setup(name="sample"';
|
|
61
|
+
expect(parseSetupPy(source).size).toBeGreaterThan(0);
|
|
62
|
+
answer(false);
|
|
63
|
+
expect(await packageNamesFromManifest(source, 'python')).toEqual(new Set());
|
|
64
|
+
});
|
|
65
|
+
it('posts the source to the validation route and keeps a valid answer', async () => {
|
|
66
|
+
const source = '[project]\nname = "my-package"\n';
|
|
67
|
+
answer(true);
|
|
68
|
+
const names = await packageNamesFromManifest(source, 'toml');
|
|
69
|
+
expect(makeRequest).toHaveBeenCalledWith('/vscode-icons/validate-manifest', expect.objectContaining({
|
|
70
|
+
method: 'POST',
|
|
71
|
+
body: JSON.stringify({ kind: 'toml', source })
|
|
72
|
+
}), expect.anything());
|
|
73
|
+
expect(names.has('my-package')).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
it('keeps the local names when the route is absent', async () => {
|
|
76
|
+
// A frontend-only install has no server half: the call 404s and the
|
|
77
|
+
// local parse stands, silently, as it did before the validation existed.
|
|
78
|
+
const source = '[project]\nname = "my-package"\n';
|
|
79
|
+
makeRequest.mockResolvedValue({
|
|
80
|
+
ok: false,
|
|
81
|
+
json: async () => ({})
|
|
82
|
+
});
|
|
83
|
+
const names = await packageNamesFromManifest(source, 'toml');
|
|
84
|
+
expect(names.has('my-package')).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
it('keeps the local names when the call cannot complete', async () => {
|
|
87
|
+
const source = 'setup(name="my-package")';
|
|
88
|
+
makeRequest.mockRejectedValue(new Error('network'));
|
|
89
|
+
const names = await packageNamesFromManifest(source, 'python');
|
|
90
|
+
expect(names.has('my-package')).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -39,7 +39,9 @@ const harness = async () => {
|
|
|
39
39
|
docRegistry: {
|
|
40
40
|
addFileType: () => ({ dispose: () => undefined }),
|
|
41
41
|
getFileTypesForPath: () => [],
|
|
42
|
-
getFileType: () => undefined
|
|
42
|
+
getFileType: () => undefined,
|
|
43
|
+
getFileTypeForModel: () => ({ name: 'text' }),
|
|
44
|
+
fileTypes: () => []
|
|
43
45
|
},
|
|
44
46
|
shell: { add: () => undefined, currentWidget: null },
|
|
45
47
|
commands: { addCommand: () => undefined },
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Settings schema defaults - ACC-CONFIG-5.
|
|
3
3
|
*
|
|
4
4
|
* The seven toggles gate which icon groups paint. Six default on; executable
|
|
5
|
-
* icons default off
|
|
6
|
-
* directory.
|
|
5
|
+
* icons default off per ACC-CONFIG-5.
|
|
7
6
|
*/
|
|
8
7
|
export {};
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Settings schema defaults - ACC-CONFIG-5.
|
|
3
3
|
*
|
|
4
4
|
* The seven toggles gate which icon groups paint. Six default on; executable
|
|
5
|
-
* icons default off
|
|
6
|
-
* directory.
|
|
5
|
+
* icons default off per ACC-CONFIG-5.
|
|
7
6
|
*/
|
|
8
7
|
import schema from '../../schema/plugin.json';
|
|
9
8
|
const properties = schema.properties;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
2
|
+
export declare function packageNamesFromManifest(source: string, kind: 'toml' | 'python', cacheKey?: string): Promise<Set<string>>;
|
|
2
3
|
declare const plugin: JupyterFrontEndPlugin<void>;
|
|
3
4
|
export default plugin;
|