UNPKG

generator-code

Version:

Yeoman generator for Visual Studio Code extensions.

316 lines (299 loc) 12.9 kB
/*--------------------------------------------------------- * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ import Generator from 'yeoman-generator'; import * as prompts from './prompts.js'; import * as validator from './validator.js'; import * as path from 'path'; import * as fs from 'fs'; import * as plistParser from 'fast-plist'; import request from 'request-light'; /** * @typedef {{ * themeContent: Object, * themeName: string, * themeBase: string, * themeFileName: string, * tmThemeFileName: string, * tmThemeContent: string, * } & import('./index.js').ExtensionConfig} ExtensionConfig */ /** * @type {import('./index.js').ExtensionGenerator} */ export default { id: 'ext-colortheme', aliases: ['colortheme'], name: 'New Color Theme', /** * @param {Generator} generator * @param {ExtensionConfig} extensionConfig */ prompting: async (generator, extensionConfig) => { await askForThemeInfo(generator, extensionConfig); await prompts.askForExtensionDisplayName(generator, extensionConfig); await prompts.askForExtensionId(generator, extensionConfig); await prompts.askForExtensionDescription(generator, extensionConfig); const nameAnswer = await generator.prompt({ type: 'input', name: 'themeName', message: 'What\'s the name of your theme shown to the user?', default: extensionConfig.themeName, validate: validator.validateNonEmpty }); extensionConfig.themeName = nameAnswer.themeName; const themeBase = await generator.prompt({ type: 'list', name: 'themeBase', message: 'Select a base theme:', choices: [{ name: "Dark", value: "vs-dark" }, { name: "Light", value: "vs" }, { name: "High Contrast Dark", value: "hc-black" }, { name: "High Contrast Light", value: "hc-light" } ] }); extensionConfig.themeBase = themeBase.themeBase; await prompts.askForGit(generator, extensionConfig); }, /** * @param {Generator} generator * @param {ExtensionConfig} extensionConfig */ writing: (generator, extensionConfig) => { if (extensionConfig.tmThemeFileName) { generator.fs.copyTpl(generator.templatePath('themes/theme.tmTheme'), generator.destinationPath('themes', extensionConfig.tmThemeFileName), extensionConfig); } extensionConfig.themeFileName = validator.sanitizeFilename(extensionConfig.themeName + '-color-theme.json'); if (extensionConfig.themeContent) { extensionConfig.themeContent.name = extensionConfig.themeName; generator.fs.copyTpl(generator.templatePath('themes/color-theme.json'), generator.destinationPath('themes', extensionConfig.themeFileName), extensionConfig); } else { generator.fs.copyTpl(generator.templatePath('themes/new-' + extensionConfig.themeBase + '-color-theme.json'), generator.destinationPath('themes', extensionConfig.themeFileName), extensionConfig); } generator.fs.copy(generator.templatePath('vscode'), generator.destinationPath('.vscode')); generator.fs.copyTpl(generator.templatePath('package.json'), generator.destinationPath('package.json'), extensionConfig); generator.fs.copyTpl(generator.templatePath('vsc-extension-quickstart.md'), generator.destinationPath('vsc-extension-quickstart.md'), extensionConfig); generator.fs.copyTpl(generator.templatePath('README.md'), generator.destinationPath('README.md'), extensionConfig); generator.fs.copyTpl(generator.templatePath('CHANGELOG.md'), generator.destinationPath('CHANGELOG.md'), extensionConfig); generator.fs.copy(generator.templatePath('.vscodeignore'), generator.destinationPath('.vscodeignore')); if (extensionConfig.gitInit) { generator.fs.copy(generator.templatePath('gitignore'), generator.destinationPath('.gitignore')); generator.fs.copy(generator.templatePath('.gitattributes'), generator.destinationPath('.gitattributes')); } } } /** * @param {Generator} generator * @param {ExtensionConfig} extensionConfig */ async function askForThemeInfo(generator, extensionConfig) { if (generator.options['quick']) { return Promise.resolve(); } const answer = await generator.prompt({ type: 'list', name: 'themeImportType', message: 'Do you want to import or convert an existing TextMate color theme?', choices: [ { name: 'No, start fresh', value: 'new' }, { name: 'Yes, import an existing theme but keep it as tmTheme file.', value: 'import-keep' }, { name: 'Yes, import an existing theme and inline it in the Visual Studio Code color theme file.', value: 'import-inline' } ] }); let type = answer.themeImportType; if (type === 'import-keep' || type === 'import-inline') { generator.log("Enter the location (URL (http, https) or file name) of the tmTheme file, e.g., http://www.monokai.nl/blog/wp-content/asdev/Monokai.tmTheme."); const urlAnswer = await generator.prompt({ type: 'input', name: 'themeURL', message: 'URL or file name to import:' }); await convertTheme(urlAnswer.themeURL, extensionConfig, type === 'import-inline', generator); } else { await convertTheme(null, extensionConfig, false, generator); } } /** * @param {string} location * @param {ExtensionConfig} extensionConfig * @param {boolean} inline * @param {Generator} generator */ function convertTheme(location, extensionConfig, inline, generator) { if (!location) { extensionConfig.tmThemeFileName = ''; extensionConfig.tmThemeContent = ''; } else if (location.match(/\w*:\/\//)) { // load from url return request.xhr({ url: location }).then(r => { if (r.status == 200) { let tmThemeFileName = null; if (!inline) { let contentDisposition = r.headers && r.headers['content-disposition']; if (Array.isArray(contentDisposition)) { contentDisposition = contentDisposition[0]; } if (typeof contentDisposition === 'string') { const fileNameMatch = contentDisposition.match(/filename="([^"]*)/); if (fileNameMatch) { tmThemeFileName = fileNameMatch[1]; } } if (!tmThemeFileName) { const lastSlash = location.lastIndexOf('/'); if (lastSlash) { tmThemeFileName = location.substr(lastSlash + 1); } else { tmThemeFileName = 'theme.tmTheme'; } } } return processContent(extensionConfig, tmThemeFileName, r.responseText, generator); } else { return Promise.reject("Problems loading theme: HTTP status " + r.status); } }); } else { // load from disk let body = null; try { body = fs.readFileSync(location); } catch (error) { return Promise.reject("Problems loading theme: " + error.message); } if (body) { let fileName = null; if (!inline) { fileName = path.basename(location); } return processContent(extensionConfig, fileName, body.toString(), generator); } else { return Promise.reject("Problems loading theme: Not found"); } } } /** * @param {ExtensionConfig} extensionConfig * @param {string} tmThemeFileName * @param {string} body * @param {Generator} generator */ function processContent(extensionConfig, tmThemeFileName, body, generator) { const themeNameMatch = body.match(/<key>name<\/key>\s*<string>([^<]*)/); const themeName = themeNameMatch ? themeNameMatch[1] : ''; try { extensionConfig.themeContent = migrate(body, tmThemeFileName, generator); if (tmThemeFileName) { if (tmThemeFileName.indexOf('.tmTheme') === -1) { tmThemeFileName = tmThemeFileName + '.tmTheme'; } extensionConfig.tmThemeFileName = tmThemeFileName; extensionConfig.tmThemeContent = body; } extensionConfig.themeName = themeName; extensionConfig.displayName = themeName; return Promise.resolve(); } catch (e) { return Promise.reject(e); } }; // mapping from old tmTheme setting to new workbench color ids const mappings = { "background": ["editor.background"], "foreground": ["editor.foreground"], "hoverHighlight": ["editor.hoverHighlightBackground"], "linkForeground": ["editorLink.foreground"], "selection": ["editor.selectionBackground"], "inactiveSelection": ["editor.inactiveSelectionBackground"], "selectionHighlightColor": ["editor.selectionHighlightBackground"], "wordHighlight": ["editor.wordHighlightBackground"], "wordHighlightStrong": ["editor.wordHighlightStrongBackground"], "findMatchHighlight": ["editor.findMatchHighlightBackground", "peekViewResult.matchHighlightBackground"], "currentFindMatchHighlight": ["editor.findMatchBackground"], "findRangeHighlight": ["editor.findRangeHighlightBackground"], "referenceHighlight": ["peekViewEditor.matchHighlightBackground"], "lineHighlight": ["editor.lineHighlightBackground"], "rangeHighlight": ["editor.rangeHighlightBackground"], "caret": ["editorCursor.foreground"], "invisibles": ["editorWhitespace.foreground"], "guide": ["editorIndentGuide.background"], "ansiBlack": ["terminal.ansiBlack"], "ansiRed": ["terminal.ansiRed"], "ansiGreen": ["terminal.ansiGreen"], "ansiYellow": ["terminal.ansiYellow"], "ansiBlue": ["terminal.ansiBlue"], "ansiMagenta": ["terminal.ansiMagenta"], "ansiCyan": ["terminal.ansiCyan"], "ansiWhite": ["terminal.ansiWhite"], "ansiBrightBlack": ["terminal.ansiBrightBlack"], "ansiBrightRed": ["terminal.ansiBrightRed"], "ansiBrightGreen": ["terminal.ansiBrightGreen"], "ansiBrightYellow": ["terminal.ansiBrightYellow"], "ansiBrightBlue": ["terminal.ansiBrightBlue"], "ansiBrightMagenta": ["terminal.ansiBrightMagenta"], "ansiBrightCyan": ["terminal.ansiBrightCyan"], "ansiBrightWhite": ["terminal.ansiBrightWhite"] }; /** * @param {string} content * @param {string} tmThemeFileName * @param {Generator} generator */ function migrate(content, tmThemeFileName, generator) { let result = {}; let theme; try { theme = plistParser.parse(content); } catch (e) { throw new Error(tmThemeFileName + " not be parsed: " + e.toString()); } let settings = theme.settings; if (Array.isArray(settings)) { let colorMap = {}; for (let entry of settings) { let scope = entry.scope; if (scope) { let parts = scope.split(',').map(p => p.trim()); if (parts.length > 1) { entry.scope = parts; } } else { const entrySettings = entry.settings; let notSupported = []; for (let entry in entrySettings) { let mapping = mappings[entry]; if (mapping) { for (let newKey of mapping) { colorMap[newKey] = entrySettings[entry]; } if (entry !== 'foreground' && entry !== 'background') { delete entrySettings[entry]; } } else { notSupported.push(entry); } } if (notSupported.length > 0) { generator.log('Note: the following theming properties are not supported by VS Code and will be ignored: ' + notSupported.join(', ')) } } } if (!tmThemeFileName) { result.tokenColors = settings; } else { result.tokenColors = './' + tmThemeFileName; } result.colors = colorMap; } return result; };