Skip to content

Commit

Permalink
move snippets loading to snippetLoader.ts (#4 and #6)
Browse files Browse the repository at this point in the history
  • Loading branch information
RandomFractals committed Mar 23, 2021
1 parent 6dd6786 commit 1060e20
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 122 deletions.
8 changes: 2 additions & 6 deletions src/commands.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import {
ExtensionContext,
Uri,
commands,
window,
workspace
commands
}
from 'vscode';
import {ISnippet} from './snippets/snippet';

export function registerCommands(context: ExtensionContext) {
context.subscriptions.push(
commands.registerCommand(`snippets.viewer.insertSnippet`, (snippetBody: ISnippet['body']) => {
commands.registerCommand(`snippets.viewer.insertSnippet`, (snippetBody: string | string[]) => {
let snippetAsString;
if (Array.isArray(snippetBody)) {
snippetAsString = snippetBody.join('\n');
Expand Down
15 changes: 8 additions & 7 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@ import {
window
}
from 'vscode';
import * as path from 'path';
import {registerCommands} from './commands';
import {SnippetLoader} from './snippets/snippetLoader';
import {SnippetTreeDataProvider} from './snippets/snippetTreeDataProvider';

export function activate(context: ExtensionContext) {
// create snippets sidebar view section
const snippetsProvider = new SnippetTreeDataProvider();
const snippetLoader: SnippetLoader = new SnippetLoader();
// create snippets tree view
const snippetProvider = new SnippetTreeDataProvider(snippetLoader);
window.createTreeView('snippets.view', {
treeDataProvider: snippetsProvider,
treeDataProvider: snippetProvider,
showCollapseAll: false,
});

context.subscriptions.push(
commands.registerCommand(`snippets.viewer.refreshSnippets`, () => snippetsProvider.refresh(true))
commands.registerCommand(`snippets.viewer.refreshSnippets`, () => snippetProvider.refresh(true))
);

// add other snippts commands
// add other snippet commands
registerCommands(context);
}

Expand Down
126 changes: 126 additions & 0 deletions src/snippets/snippetLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import {
window,
extensions
} from 'vscode';
import {
SnippetLanguage,
SnippetFile,
Snippet
} from './snippets'
import * as jsonc from 'jsonc-parser';
import * as fs from 'fs';
import * as path from 'path';

export class SnippetLoader {

private _snippets: {[language: string]: Snippet[]} = {};
private _snippetFiles: SnippetFile[] = new Array<SnippetFile>();
private _snippetExtensions: {[extensionId: string]: Snippet[]} = {};

constructor() {
}

refresh(clearSnippets: boolean): void {
if (clearSnippets) {
this._snippets = {};
this._snippetExtensions = {};
this._snippetFiles = [];
}
}

async getSnippetLanguages(): Promise<SnippetLanguage[]> {
// get snippet languages from extension snippet files
const snippetLanguages: SnippetLanguage[] = [];
const snippetLanguageMap: Map<string, SnippetLanguage> = new Map<string, SnippetLanguage>();
extensions.all.forEach(extension => {
if (!extension.packageJSON.isBuiltin && extension.packageJSON?.contributes?.snippets) {
const extensionName = extension.packageJSON?.extensionName;
const extensionLocation = extension.packageJSON?.extensionLocation;
const snippetsConfig = extension.packageJSON?.contributes?.snippets;
if (extensionLocation && Array.isArray(snippetsConfig)) {
snippetsConfig.forEach(snippetConfig => {
const language: string = snippetConfig.language;
const snippetFile: SnippetFile = new SnippetFile(extensionName,
path.join(extensionLocation.fsPath, snippetConfig.path),
language
);
if (!snippetLanguageMap.has(language)) {
const snippetLanguage: SnippetLanguage = new SnippetLanguage(language);
snippetLanguages.push(snippetLanguage);
snippetLanguageMap.set(language, snippetLanguage);
}
snippetLanguageMap.get(language)?.snippetFiles.push(snippetFile);
});
}
}
});
return Promise.resolve(snippetLanguages);
}

async getSnippetFiles(extensionId: string): Promise<SnippetFile[]> {
const extension = extensions.getExtension(extensionId);
let snippetFiles: SnippetFile[] = [];
if (!this._snippetExtensions[extensionId]) {
this._snippetExtensions[extensionId] = [];
}
if (extension) {
const extensionLocation = extension.packageJSON?.extensionLocation;
const snippetsConfig = extension.packageJSON?.contributes?.snippets;
if (extensionLocation && Array.isArray(snippetsConfig)) {
snippetsConfig.forEach(snippetConfig => {
const snippetFile: SnippetFile = new SnippetFile(
snippetConfig.language,
path.join(extensionLocation.fsPath, snippetConfig.path),
snippetConfig.language
);
snippetFiles.push(snippetFile);
this._snippetFiles.push(snippetFile);
});
await Promise.all(snippetFiles.map((file: SnippetFile) => this.getSnippets(file, extensionId)));
}
}
return Promise.resolve(snippetFiles);
}

async getSnippets(snippetFile: SnippetFile, extensionId?: string): Promise<Snippet[]> {
if (!this._snippets[snippetFile.language]) {
// create new language snippets array
this._snippets[snippetFile.language] = [];
}
return new Promise((resolve, reject) => {
fs.readFile(snippetFile.filePath, 'utf8', (error, snippetsConfig) => {
if (error) {
window.showErrorMessage(`Error reading file ${snippetFile.filePath} \n ${error.message}`);
return reject([]);
}
if (snippetsConfig === '') {
return resolve([]);
}

let parsedSnippets: any;
try {
parsedSnippets = jsonc.parse(snippetsConfig); // tslint:disable-line
}
catch (err) {
window.showErrorMessage(`JSON parsing of snippet file ${snippetFile.filePath} failed`);
return reject([]);
}

// load parsed snippets
const snippets: Snippet[] = [];
for (const key in parsedSnippets) {
const parsedSnippet = parsedSnippets[key];
const scope = [snippetFile.language];
const snippet: Snippet =
new Snippet(key, parsedSnippet.prefix, scope, parsedSnippet.body, snippetFile);
snippets.push(snippet);
this._snippets[snippetFile.language].push(snippet);
if (extensionId) {
this._snippetExtensions[extensionId].push(snippet);
}
}
return resolve(snippets);
});
});
}
}
115 changes: 8 additions & 107 deletions src/snippets/snippetTreeDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,23 @@ import {
SnippetLanguage,
SnippetFile,
Snippet
} from './snippets'
}
from './snippets'
import {SnippetLoader} from './snippetLoader';
import * as jsonc from 'jsonc-parser';
import * as fs from 'fs';
import * as path from 'path';

export class SnippetTreeDataProvider implements TreeDataProvider<SnippetLanguage | SnippetFile | Snippet> {
private readonly _onDidChangeTreeData: EventEmitter<Snippet | undefined> = new EventEmitter<Snippet | undefined>();
readonly onDidChangeTreeData: Event<Snippet | undefined> = this._onDidChangeTreeData.event;
private _snippets: {[language: string]: Snippet[]} = {};
private _snippetFiles: SnippetFile[] = new Array<SnippetFile>();
private _snippetExtensions: {[extensionId: string]: Snippet[]} = {};

constructor() {
constructor(private snippetLoader: SnippetLoader) {
}

refresh(clearSnippets: boolean): void {
if (clearSnippets) {
this._snippets = {};
this._snippetExtensions = {};
this._snippetFiles = [];
this.snippetLoader.refresh(clearSnippets);
}
this._onDidChangeTreeData.fire(undefined);
}
Expand All @@ -40,114 +37,18 @@ export class SnippetTreeDataProvider implements TreeDataProvider<SnippetLanguage

async getChildren(element?: SnippetLanguage | SnippetFile): Promise<SnippetLanguage[] | SnippetFile[] | Snippet[]> {
if (!element) {
return await this.getSnippetLanguages();
return await this.snippetLoader.getSnippetLanguages();
}
else if (element.contextValue === 'snippetLanguage') {
return (element as SnippetLanguage).snippetFiles;
}
else if (element.contextValue === 'snippetFile') {
const snippets = await this.getSnippets(element as SnippetFile);
const snippets = await this.snippetLoader.getSnippets(element as SnippetFile);
return snippets.sort(this.sortByScope);
}
return [];
}

private async getSnippetLanguages(): Promise<SnippetLanguage[]> {
// get snippet languages from extension snippet files
const snippetLanguages: SnippetLanguage[] = [];
const snippetLanguageMap: Map<string, SnippetLanguage> = new Map<string, SnippetLanguage>();
extensions.all.forEach(extension => {
if (!extension.packageJSON.isBuiltin && extension.packageJSON?.contributes?.snippets) {
const extensionName = extension.packageJSON?.extensionName;
const extensionLocation = extension.packageJSON?.extensionLocation;
const snippetsConfig = extension.packageJSON?.contributes?.snippets;
if (extensionLocation && Array.isArray(snippetsConfig)) {
snippetsConfig.forEach(snippetConfig => {
const language: string = snippetConfig.language;
const snippetFile: SnippetFile = new SnippetFile(extensionName,
path.join(extensionLocation.fsPath, snippetConfig.path),
language
);
if (!snippetLanguageMap.has(language)) {
const snippetLanguage: SnippetLanguage = new SnippetLanguage(language);
snippetLanguages.push(snippetLanguage);
snippetLanguageMap.set(language, snippetLanguage);
}
snippetLanguageMap.get(language)?.snippetFiles.push(snippetFile);
});
}
}
});
return Promise.resolve(snippetLanguages);
}

private async getSnippetFiles(extensionId: string): Promise<SnippetFile[]> {
const extension = extensions.getExtension(extensionId);
let snippetFiles: SnippetFile[] = [];
if (!this._snippetExtensions[extensionId]) {
this._snippetExtensions[extensionId] = [];
}
if (extension) {
const extensionLocation = extension.packageJSON?.extensionLocation;
const snippetsConfig = extension.packageJSON?.contributes?.snippets;
if (extensionLocation && Array.isArray(snippetsConfig)) {
snippetsConfig.forEach(snippetConfig => {
const snippetFile: SnippetFile = new SnippetFile(
snippetConfig.language,
path.join(extensionLocation.fsPath, snippetConfig.path),
snippetConfig.language
);
snippetFiles.push(snippetFile);
this._snippetFiles.push(snippetFile);
});
await Promise.all(snippetFiles.map((file: SnippetFile) => this.getSnippets(file, extensionId)));
}
}
return Promise.resolve(snippetFiles);
}

private async getSnippets(snippetFile: SnippetFile, extensionId?: string): Promise<Snippet[]> {
if (!this._snippets[snippetFile.language]) {
// create new language snippets array
this._snippets[snippetFile.language] = [];
}
return new Promise((resolve, reject) => {
fs.readFile(snippetFile.filePath, 'utf8', (error, snippetsConfig) => {
if (error) {
window.showErrorMessage(`Error reading file ${snippetFile.filePath} \n ${error.message}`);
return reject([]);
}
if (snippetsConfig === '') {
return resolve([]);
}

let parsedSnippets: any;
try {
parsedSnippets = jsonc.parse(snippetsConfig); // tslint:disable-line
}
catch (err) {
window.showErrorMessage(`JSON parsing of snippet file ${snippetFile.filePath} failed`);
return reject([]);
}

// load parsed snippets
const snippets: Snippet[] = [];
for (const key in parsedSnippets) {
const parsedSnippet = parsedSnippets[key];
const scope = [snippetFile.language];
const snippet: Snippet =
new Snippet(key, parsedSnippet.prefix, scope, parsedSnippet.body, snippetFile);
snippets.push(snippet);
this._snippets[snippetFile.language].push(snippet);
if (extensionId) {
this._snippetExtensions[extensionId].push(snippet);
}
}
return resolve(snippets);
});
});
}

private readonly filterSnippets = (snippet: Snippet): boolean => {
// filter snippets by language for the active text editor
if (window.activeTextEditor && snippet.scope.length !== 0) {
Expand All @@ -163,4 +64,4 @@ export class SnippetTreeDataProvider implements TreeDataProvider<SnippetLanguage
const n2 = sn2.scope.length === 1 ? Infinity : sn2.scope.length;
return n2 - n1;
}
}
}
5 changes: 3 additions & 2 deletions src/snippets/snippets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
TreeItemCollapsibleState,
ThemeIcon,
Uri
} from 'vscode';
}
from 'vscode';

export class Snippet extends TreeItem {
readonly collapsibleState = TreeItemCollapsibleState.None;
Expand All @@ -28,7 +29,7 @@ export class Snippet extends TreeItem {
this.command = {
command: `snippets.viewer.insertSnippet`,
title: 'Insert Snippet',
arguments: [body],
arguments: [body]
};
}
}
Expand Down

0 comments on commit 1060e20

Please sign in to comment.