Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,12 @@
{
"label": "Build Voodoo Theme",
"detail": "Build Voodoo theme's file from source",
"command": "echo ${input:build}",
"command": "node ./builder build-theme",
"type": "shell",
"group": {
"kind": "build",
"isDefault": true
}
}
],
"inputs": [
{
"id": "build",
"type": "command",
"command": "voodoo.buildTheme"
}
]
}
}
2 changes: 1 addition & 1 deletion .vscodeignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.vscode/
.editorconfig
.gitignore
theme-src.schema.json
vsc-extension-quickstart.md
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## Version 0.11.0
- Update the builder's workflow so it's easier to use
- Add instructions to README.md

## Version 0.10.0
- Add builder extension to help maintain and adjust the theme
- Command to build the theme's file from `themes/theme-src.json` which supports comments and variables
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,27 @@
<p align="center">Issues and PRs are welcome! 👻</p>

![](https://github.com/liamsheppard/voodoo-theme/blob/master/images/main.png?raw=true)

---

## How to contribute

Voodoo is built from `./themes/Voodoo-color-source.json`, a file that contains the theme's color palette as a dictionary of variables and color codes which are then used to stylize multiple syntax scopes and tokens at once.

To build the theme yourself, all you need is to execute the package's pre-defined scripts as follow:

```sh
# You'll need some dependencies which aren't shipped with the extension, aka the strip-json-comments module.
yarn install # or npm install

# Then:
yarn build # or npm run build
```

The theme's builder will read the `./themes/Voodoo-color-source.json` source file from either the active vscode's workspace or its own root path (`./builder/../themes/`). The resulting file is then output in the same directory as `Voodoo-color-theme.json`.

Conversely, the recommended setup is to simply grab the theme from its git repository and work from there. Working directly from the repository's also comes with extra benefits such as pre-configured `.vscode/*.json` settings to debug using the extension host and build using the "Build Task" or the `ctrl+shift+b` shortcut.

Finally, the builder uses the `./builder/vscode-extension.js` module as its entry point to generate `./themes/Voodoo-color-source.schema.json`, a json schema file which is used to validate the theme's source file formatting as well as provide useful insight and descriptions while editing.

The above-mentioned `*.schema.json` file is automatically generated (and used) by the extension when the `./themes/Voodoo-color-source.json` file is detected in the active vscode window's workspace or when the extension is run / debug if working from a repository clone.
55 changes: 41 additions & 14 deletions builder/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ class ParsedThemeObject {

source.useRawValues = useRawValues;

this._parseColors(source);
this._parseTokenColors(source);
this.#parseColors(source);
this.#parseTokenColors(source);
}

_parseVariable(value, source) {
#parseVariable(value, source) {
const components = value.split(" ");

// Check if variable exists.
Expand Down Expand Up @@ -70,7 +70,7 @@ class ParsedThemeObject {
: (value += !isNaN(alpha) ? parseInt(alpha * 255).toString(16) : "00");
}

_parseTokenColors(source) {
#parseTokenColors(source) {
// Check if the theme's source does contain token colors.
if (!source.hasOwnProperty("tokenColors")) {
return;
Expand All @@ -90,13 +90,13 @@ class ParsedThemeObject {
return;
}

scope.settings.foreground = this._parseVariable(scope.settings.foreground, source);
scope.settings.foreground = this.#parseVariable(scope.settings.foreground, source);
});

this.tokenColors = tokenColors;
}

_parseColors(source) {
#parseColors(source) {
// Check if the theme's source does contain colors.
if (!source.hasOwnProperty("colors")) {
return;
Expand All @@ -109,28 +109,55 @@ class ParsedThemeObject {

// Parse colors from the source's colors and variables properties.
Object.entries(source.colors).forEach(([key, value]) => {
this.colors[key] = this._parseVariable(value, source);
this.colors[key] = this.#parseVariable(value, source);
});
}
}

const { readFile, writeFile } = require("fs").promises;
const vscode = require("vscode");
const paths = require("./paths/paths");
const logger = require("./logger");

/**
* Retrieves the json schema for the Voodoo theme's source file from
* vscode's built-in color theme schema and returns the resulting parsed
* theme object.
*/
async function buildThemeFile() {
const themeSourcePath = (await vscode.workspace.findFiles("**/themes/theme-src.json"))[0];
const themeSource = await readFile(themeSourcePath.fsPath, "utf-8");
logger.log("Build started...", "build");
logger.log(`Will be using '${paths.getInstanceType()}' to retrieve paths.`, "build");

const themeSourcesPaths = await paths.getThemeSourcesPaths();
let themeSourcePath = undefined;

switch (themeSourcesPaths.length) {
case 1:
themeSourcePath = themeSourcesPaths[0];
break;

case 0:
logger.log("BUILD FAILED: no suitable Voodoo theme source file found in the current workspace!\n", "build");
await paths.hintPotentialThemeSourcesPaths();
return;

default:
logger.log("BUILD FAILED: found multiple suitable Voodoo theme source files in the current workspace! Please keep a single active source file under the '**/themes/' directory before building.\n", "build");
await paths.hintSuitableThemeSourcesPaths();
return;
}

logger.log(`Reading theme from '${themeSourcePath}'`, "build");

const themeSource = await readFile(themeSourcePath, "utf-8");
const themeObject = new ParsedThemeObject(themeSource);

const themePath =
themeSourcePath.fsPath.slice(0, -"theme-src.json".length) + "Voodoo-color-theme.json";
const buildPath = paths.getBuildPathFrom(themeSourcePath);

logger.log(`Writing theme to '${buildPath}'`, "build");

await writeFile(buildPath, JSON.stringify(themeObject) + "\n", "utf-8");

await writeFile(themePath, JSON.stringify(themeObject) + "\n", "utf-8");
logger.log("BUILD SUCCEEDED.\n", "build");
}

module.exports = { buildThemeFile, ParsedThemeObject };
module.exports = { buildThemeFile, ParsedThemeObject };
67 changes: 0 additions & 67 deletions builder/extension.js

This file was deleted.

19 changes: 19 additions & 0 deletions builder/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
if (process.argv.length < 3) {
let errorMsg = "Unexpected argv.length! Expected { [0]: node's execPath, [1]: builder's path, [2]: command }, but got: ";
process.argv.forEach((value, index) => errorMsg += `\n - [${index}]: ${value}`);

console.error(errorMsg);
return;
}

const cmd = process.argv[2];

switch (cmd) {
case "build-theme":
require("./build").buildThemeFile();
break;

default:
console.error(`Unsupported command: '${cmd}'! - usage: ./builder build-theme`);
break;
}
63 changes: 63 additions & 0 deletions builder/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
class VoodooLogger {
#vscodeOutputChannel = undefined;
#notInVscode = false;

/**
* @returns {vscode.OutputChannel} the vscode output channel used by the extension.
*/
get outputChannel() {
if (this.#vscodeOutputChannel != undefined || this.#notInVscode) {
return this.#vscodeOutputChannel
}

try {
const vscode = require("vscode");

this.#vscodeOutputChannel = vscode.window.createOutputChannel("voodoo-builder");
this.#vscodeOutputChannel.show();

vscode.window.showInformationMessage("Extension details logged in 'Output > voodoo-builder'.");
} catch (error) {
if (error.code != "MODULE_NOT_FOUND") {
throw error;
}

this.#notInVscode = true;
}

return this.#vscodeOutputChannel;
}

/**
* Logs the passed message in the extension's output channel when called
* from a vscode extension and in the JS console.
* @param {string} message the message to log.
*/
log(message) {
console.log(message);

if (!this.#notInVscode && this.outputChannel != undefined) {
this.outputChannel.appendLine(message);
}
}
}

const logger = new VoodooLogger();

function getCurrentDateTime() {
const dtNow = new Date();
const dtLocalOffsetInMs = dtNow.getTimezoneOffset() * 60 * 1000;
const dtLocalIsoString = new Date(dtNow.getTime() - dtLocalOffsetInMs).toISOString();
return dtLocalIsoString.replace("T", " ").replace("Z", "");
}

/**
* Logs the passed message in the extension's output channel with an `info` tag.
* @param {string} message the log's content.
* @param {string} process the logging process.
*/
function log(message, process = undefined) {
logger.log(`[${getCurrentDateTime()}]${process ? ` [${process}] ` : " "}${message}`);
}

module.exports = { log };
Loading