diff --git a/package.json b/package.json index bac8351..455b607 100644 --- a/package.json +++ b/package.json @@ -17,30 +17,31 @@ "Snippets" ], "activationEvents": [ - "onCommand:extension.createClass", - "onCommand:extension.createInterface" + "onCommand:extension.createFromList", + "onCommand:extension.createTemplate", + "onCommand:extension.editTemplate" ], "main": "./out/src/extension", "contributes": { "commands": [ { - "command": "extension.createClass", - "title": "New C# Class" + "command": "extension.createFromList", + "title": "New C# File" }, { - "command": "extension.createInterface", - "title": "New C# Interface" + "command": "extension.createTemplate", + "title": "Create New C# Template" + }, + { + "command": "extension.editTemplate", + "title": "Edit Existing C# Template" } ], "menus": { "explorer/context": [ { "group": "navigation@-1", - "command": "extension.createClass" - }, - { - "group": "navigation@-1", - "command": "extension.createInterface" + "command": "extension.createFromList" } ] } diff --git a/src/TemplateManager.ts b/src/TemplateManager.ts new file mode 100644 index 0000000..a7fdb3f --- /dev/null +++ b/src/TemplateManager.ts @@ -0,0 +1,76 @@ +import {TemplateQuickPickItem} from './TemplateQuickPickItem'; +import { execFile } from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; + +export class TemplateManager { + + templateDirectory = vscode.extensions.getExtension('jchannon.csharpextensions').extensionPath + '/templates/'; + + installMessage = 'Install'; + + createTemplate() { + vscode.window.showInputBox({ ignoreFocusOut: true, prompt: 'Please enter template name', value: "templateName.tmpl" }).then(result => { + let newTemplateFileName = path.join(this.templateDirectory, result); + this.createNewTemplateFile(newTemplateFileName); + this.openTemplate(newTemplateFileName); + }); + } + + editTemplate() { + this.showTemplateQuickPick() + .then(selectedTemplate => { + var templateFileName = path.join(this.templateDirectory, selectedTemplate.template + '.tmpl'); + this.openTemplate(templateFileName); + }); + } + + openTemplate(template: string) { + execFile('code', [this.templateDirectory, template], (error: any, stdout, stderr) => { + if (error.code === 'ENOENT') { + vscode.window.showErrorMessage('Please install \'code\' command into the PATH', this.installMessage).then(value => { + if (value === this.installMessage) { + vscode.commands.executeCommand('workbench.action.installCommandLine'); + } + }); + } + }); + } + + showTemplateQuickPick(): Thenable { + let templates = this.getTemplateQuickPickItems(); + return vscode.window.showQuickPick(Promise.all(templates) as Thenable>); + } + + getTemplateQuickPickItems(): PromiseLike[] { + let templateDirectory = vscode.extensions.getExtension('jchannon.csharpextensions').extensionPath + '/templates/'; + return fs.readdirSync(templateDirectory).map(file => { + let templateName = path.basename(file, '.tmpl'); + return vscode.workspace.openTextDocument(path.join(templateDirectory, file)).then(doc => { + let text = doc.getText(); + let regex = /\$\{Description:(.*)\}/i; + let description = text.match(regex)[1]; + return new TemplateQuickPickItem(templateName, description, "", templateName); + }); + }); + } + + createNewTemplateFile(newTemplateFile: string) { + if (!fs.exists(newTemplateFile)) { + fs.writeFileSync(newTemplateFile, `\${Description: Template Description Here} +//use \${namespace} to set namespace +//use \${name} to set name +//use \${cursor} to set where the cursor will go when the file is first opened + `); + } + } + + findCursorInTemlpate(text: string) { + let cursorPos = text.indexOf('${cursor}'); + let preCursor = text.substr(0, cursorPos); + let lineNum = preCursor.match(/\n/gi).length; + let charNum = preCursor.substr(preCursor.lastIndexOf('\n')).length; + return new vscode.Position(lineNum, charNum); + } +} \ No newline at end of file diff --git a/src/TemplateQuickPickItem.ts b/src/TemplateQuickPickItem.ts new file mode 100644 index 0000000..7c025e1 --- /dev/null +++ b/src/TemplateQuickPickItem.ts @@ -0,0 +1,10 @@ +import * as vscode from 'vscode'; + +export class TemplateQuickPickItem implements vscode.QuickPickItem +{ + constructor(public label: string, + public description: string, + public detail: string, + public template: string ){ + } +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index ff6d182..02be57d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,63 +1,46 @@ 'use strict'; -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from 'vscode'; -import * as path from 'path'; +import { ensureExtension } from './helpers'; +import { TemplateManager } from './TemplateManager'; import * as fs from 'fs'; import * as os from 'os'; +import * as path from 'path'; +import * as vscode from 'vscode'; +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below var parentfinder = require('find-parent-dir'); -// this method is called when your extension is activated -// your extension is activated the very first time the command is executed +const templateManager = new TemplateManager(); export function activate(context: vscode.ExtensionContext) { - - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated - //console.log('Congratulations, your extension "newclassextension" is now active!'); - - // The command has been defined in the package.json file - // Now provide the implementation of the command with registerCommand - // The commandId parameter must match the command field in package.json - // let disposable = vscode.commands.registerCommand('extension.sayHello', (args) => { - // // The code you place here will be executed every time your command is executed - // // Display a message box to the user - // vscode.window.showInformationMessage('Hello World!'); - // }); - - //context.subscriptions.push(disposable); - context.subscriptions.push(vscode.commands.registerCommand('extension.createClass', createClass)); - context.subscriptions.push(vscode.commands.registerCommand('extension.createInterface', createInterface)); + context.subscriptions.push(vscode.commands.registerCommand('extension.createFromList', createFromList)); + context.subscriptions.push(vscode.commands.registerCommand('extension.createTemplate', templateManager.createTemplate)); + context.subscriptions.push(vscode.commands.registerCommand('extension.editTemplate', templateManager.editTemplate)); } -function createClass(args) { - promptAndSave(args, 'class'); +// this method is called when your extension is deactivated +export function deactivate() { } -function createInterface(args) { - promptAndSave(args, 'interface'); +function createFromList(args) { + templateManager.showTemplateQuickPick() + .then(selectedTemplate => { + promptAndSave(args, selectedTemplate.template); + }); } function promptAndSave(args, templatetype: string) { - if (args == null) { + if (args === null) { args = { _fsPath: vscode.workspace.rootPath } } let incomingpath: string = args._fsPath; vscode.window.showInputBox({ ignoreFocusOut: true, prompt: 'Please enter filename', value: 'new' + templatetype + '.cs' }) .then((filename) => { - - if (typeof filename === 'undefined') { + if (filename === null) { return; } filename = incomingpath + path.sep + filename; - if (path.extname(filename) !== 'cs') { - if (filename.endsWith('.')) { - filename = filename + 'cs'; - } else { - filename = filename + '.cs'; - } - } + ensureExtension(filename, '.cs'); var originalfilename = filename; @@ -71,8 +54,9 @@ function promptAndSave(args, templatetype: string) { var filenamechildpath = filename.substring(filename.lastIndexOf(newroot)); var pathSepRegEx = /\//g; - if (os.platform() === "win32") + if (os.platform() === "win32") { pathSepRegEx = /\\/g; + } var namespace = path.dirname(filenamechildpath); namespace = namespace.replace(pathSepRegEx, '.'); @@ -89,12 +73,13 @@ function openTemplateAndSaveNewFile(type: string, namespace: string, filename: s let templatefileName = type + '.tmpl'; - vscode.workspace.openTextDocument(vscode.extensions.getExtension('jchannon.csharpextensions').extensionPath + '/templates/' + templatefileName) + vscode.workspace.openTextDocument(path.join(templateManager.templateDirectory, templatefileName)) .then((doc: vscode.TextDocument) => { let text = doc.getText(); text = text.replace('${namespace}', namespace); - text = text.replace('${classname}', filename); - let cursorPosition = findCursorInTemlpate(text); + text = text.replace('${name}', filename); + text = text.replace(new RegExp(/\$\{Description:(.*)\n/i), ''); + let cursorPosition = templateManager.findCursorInTemlpate(text); text = text.replace('${cursor}', ''); fs.writeFileSync(originalfilename, text); @@ -105,17 +90,4 @@ function openTemplateAndSaveNewFile(type: string, namespace: string, filename: s }); }); }); -} - -function findCursorInTemlpate(text: string) { - let cursorPos = text.indexOf('${cursor}'); - let preCursor = text.substr(0, cursorPos); - let lineNum = preCursor.match(/\n/gi).length; - let charNum = preCursor.substr(preCursor.lastIndexOf('\n')).length; - return new vscode.Position(lineNum, charNum); - -} - -// this method is called when your extension is deactivated -export function deactivate() { } \ No newline at end of file diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 0000000..46379d4 --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,12 @@ +import { extname } from 'path'; + +export function ensureExtension(filename: string, extension: string) { + if (extname(filename) !== extension) { + if (filename.endsWith('.')) { + filename = filename + extension.replace('.',''); + } else { + filename = filename + extension; + } + } + return filename; +} \ No newline at end of file diff --git a/templates/class.tmpl b/templates/class.tmpl index d0eb0c6..3970cb5 100644 --- a/templates/class.tmpl +++ b/templates/class.tmpl @@ -1,6 +1,7 @@ +${Description:Creates a basic C# class} namespace ${namespace} { - public class ${classname} + public class ${name} { ${cursor} } diff --git a/templates/interface.tmpl b/templates/interface.tmpl index 6bb4ec9..2fdc97d 100644 --- a/templates/interface.tmpl +++ b/templates/interface.tmpl @@ -1,6 +1,7 @@ +${Description:Creates a basic C# interface} namespace ${namespace} { - public interface ${classname} + public interface ${name} { ${cursor} } diff --git a/templates/templateName.tmpl b/templates/templateName.tmpl new file mode 100644 index 0000000..dd1bf20 --- /dev/null +++ b/templates/templateName.tmpl @@ -0,0 +1,5 @@ +${Description: Template Description Here} +//use ${namespace} to set namespace +//use ${name} to set name +//use ${cursor} to set where the cursor will go when the file is first opened + \ No newline at end of file diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..c57bb12 --- /dev/null +++ b/tslint.json @@ -0,0 +1,12 @@ +{ + "rules": { + "no-unused-expression": true, + "no-duplicate-variable": true, + "no-duplicate-key": true, + "no-unused-variable": true, + "curly": true, + "class-name": true, + "semicolon": ["always"], + "triple-equals": true + } +} \ No newline at end of file