diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1699ad4..2ba9696 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,13 @@
# Changelog
+### v1.1.0
+
+- Add code lens for editing files from settings
+
+ Add ability to open editor for file added to `dotfiles.files` settings using a code lens, to make it faster to update the files in settings.json. Changes made in the editor automatically update the file's content in settings.json if `dotfiles.autoUpdate` is enabled.
+
+
+- Simplify Makefile
+
### v1.0.1
- Refactor Configuration to a separate class
@@ -58,4 +67,3 @@
- Initial commit
-
diff --git a/README.md b/README.md
index 8cb2966..f386538 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,10 @@ If `dotfiles.autoUpdate` is enabled, automatically apply config from settings to
writes file `$XDG_CONFIG_HOME/path/to/file.txt` with the content of the key.
+A code lens is added to keys in settings.json under `dotfiles.files` to open the file in an editor. Saving the file in the editor will apply changes back to settings.json if `dotfiles.autoUpdate` is enabled.
+
+
+
## Extension Settings
||Description|Default|
diff --git a/images/code-lens.png b/images/code-lens.png
new file mode 100644
index 0000000..35e6bf2
Binary files /dev/null and b/images/code-lens.png differ
diff --git a/package-lock.json b/package-lock.json
index 628c621..78baf4e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "dotfiles",
- "version": "1.0.1",
+ "version": "1.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dotfiles",
- "version": "1.0.1",
+ "version": "1.1.0",
"license": "MIT",
"devDependencies": {
"@types/mocha": "^10.0.6",
@@ -17,6 +17,7 @@
"@vscode/test-cli": "^0.0.6",
"@vscode/test-electron": "^2.3.9",
"eslint": "^8.56.0",
+ "jsonc-parser": "^3.2.1",
"typescript": "^5.3.3"
},
"engines": {
@@ -1777,6 +1778,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/jsonc-parser": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
+ "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==",
+ "dev": true
+ },
"node_modules/jszip": {
"version": "3.10.1",
"dev": true,
diff --git a/package.json b/package.json
index f37ce40..4ce29cb 100644
--- a/package.json
+++ b/package.json
@@ -8,15 +8,19 @@
},
"description": "Apply dotfiles from settings",
"icon": "images/icon-small.png",
- "version": "1.0.1",
+ "version": "1.1.0",
"license": "MIT",
- "preview": true,
"engines": {
"vscode": "^1.74.0"
},
"categories": [
"Other"
],
+ "author": {
+ "name": "grg",
+ "url": "https://grg.app",
+ "email": "vscode@grg.app"
+ },
"homepage": "https://github.com/grgar/vscode-dotfiles",
"qna": "https://github.com/grgar/vscode-dotfiles/discussions",
"bugs": {
@@ -68,14 +72,15 @@
"test": "vscode-test"
},
"devDependencies": {
- "@types/vscode": "^1.74.0",
"@types/mocha": "^10.0.6",
"@types/node": "18.x",
+ "@types/vscode": "^1.74.0",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
- "eslint": "^8.56.0",
- "typescript": "^5.3.3",
"@vscode/test-cli": "^0.0.6",
- "@vscode/test-electron": "^2.3.9"
+ "@vscode/test-electron": "^2.3.9",
+ "eslint": "^8.56.0",
+ "jsonc-parser": "^3.2.1",
+ "typescript": "^5.3.3"
}
}
diff --git a/src/Configuration.ts b/src/Configuration.ts
index 2b07ec3..ae31fa2 100644
--- a/src/Configuration.ts
+++ b/src/Configuration.ts
@@ -1,6 +1,14 @@
import path from 'path';
import * as vscode from "vscode";
+export const namespace = "dotfiles";
+
+export enum Section {
+ directory = "directory",
+ files = "files",
+ autoUpdate = "autoUpdate",
+}
+
export default class Configuration {
readonly namespace: string;
readonly logger?: (msg: string) => void;
@@ -11,7 +19,7 @@ export default class Configuration {
}
getDirectoryPath() {
- let directory = vscode.workspace.getConfiguration(this.namespace).get("directory") || process.env["XDG_CONFIG_HOME"] || path.join(process.env["HOME"]!, ".config");
+ let directory = vscode.workspace.getConfiguration(this.namespace).get(Section.directory) || process.env["XDG_CONFIG_HOME"] || path.join(process.env["HOME"]!, ".config");
if (directory.endsWith("/")) {
return directory.slice(directory.length - 1);
}
@@ -19,12 +27,12 @@ export default class Configuration {
}
getFiles() {
- return vscode.workspace.getConfiguration(this.namespace).get<{ [key: string]: string; }>("files", {});
+ return vscode.workspace.getConfiguration(this.namespace).get<{ [key: string]: string; }>(Section.files, {});
}
async setFiles(files: { [key: string]: string; }) {
try {
- await vscode.workspace.getConfiguration(this.namespace).update("files", files, vscode.ConfigurationTarget.Global);
+ await vscode.workspace.getConfiguration(this.namespace).update(Section.files, files, vscode.ConfigurationTarget.Global);
} catch (err) {
this.logger?.(`${new Date().toLocaleString()}: failed to update files: ${err}`);
vscode.window.showErrorMessage("Unable to update dotfiles.files: " + err);
@@ -32,6 +40,6 @@ export default class Configuration {
}
shouldAutoUpdate() {
- return vscode.workspace.getConfiguration(this.namespace).get("autoUpdate", false);
+ return vscode.workspace.getConfiguration(this.namespace).get(Section.autoUpdate, false);
}
}
diff --git a/src/SettingsLensProvider.ts b/src/SettingsLensProvider.ts
new file mode 100644
index 0000000..6d726ca
--- /dev/null
+++ b/src/SettingsLensProvider.ts
@@ -0,0 +1,59 @@
+import { JSONVisitor, visit } from "jsonc-parser";
+import path from "path";
+import * as vscode from "vscode";
+
+export class SettingsLensProvider implements vscode.CodeLensProvider {
+ private key: string;
+ private basePath: string;
+
+ constructor(key: string, basePath: string) {
+ this.key = key;
+ this.basePath = basePath;
+ }
+
+ provideCodeLenses(document: vscode.TextDocument): vscode.ProviderResult {
+ return parseJSONForFileLocations(this.key, document).map(
+ ({ name, range }) =>
+ new vscode.CodeLens(
+ range,
+ {
+ title: "open file",
+ command: "vscode.open",
+ arguments: [path.join(this.basePath, name)],
+ },
+ ),
+ );
+ }
+}
+
+export interface FileLocation {
+ name: string;
+ range: vscode.Range;
+}
+
+const parseJSONForFileLocations = (filesProperty: String, document: vscode.TextDocument, buffer = document.getText()): FileLocation[] => {
+ let level = 0;
+ let inFiles = false;
+ const files: FileLocation[] = [];
+ const visitor: JSONVisitor = {
+ onObjectBegin() {
+ level++;
+ },
+ onObjectEnd() {
+ inFiles = false;
+ level--;
+ },
+ onObjectProperty(property: string, offset: number, length: number) {
+ if (level === 1 && property === filesProperty) {
+ inFiles = true;
+ } else if (inFiles) {
+ files.push({
+ name: property,
+ range: new vscode.Range(document.positionAt(offset), document.positionAt(offset + length))
+ });
+ }
+ },
+ };
+ visit(buffer, visitor);
+ return files;
+};
diff --git a/src/extension.ts b/src/extension.ts
index 9ca7b53..9729624 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,9 +1,9 @@
import * as fs from "fs/promises";
import path from 'path';
import * as vscode from 'vscode';
-import Configuration from "./Configuration";
+import Configuration, { namespace as configNamespace, Section } from "./Configuration";
+import { SettingsLensProvider } from "./SettingsLensProvider";
-export const configNamespace = "dotfiles";
const outputChannel = vscode.window.createOutputChannel(configNamespace);
async function apply() {
@@ -45,11 +45,18 @@ function didSave(doc: vscode.TextDocument) {
}
export function activate(context: vscode.ExtensionContext) {
+ const configuration = new Configuration(configNamespace, outputChannel.appendLine);
context.subscriptions.push(
vscode.commands.registerCommand("dotfiles.apply", apply),
- vscode.workspace.onDidSaveTextDocument(didSave)
+ vscode.workspace.onDidSaveTextDocument(didSave),
+ vscode.languages.registerCodeLensProvider(
+ {
+ language: 'jsonc',
+ pattern: '**/settings.json',
+ },
+ new SettingsLensProvider(`${configNamespace}.${Section.files}`, configuration.getDirectoryPath()),
+ )
);
- const configuration = new Configuration(configNamespace, outputChannel.appendLine);
if (configuration.shouldAutoUpdate()) {
apply();
}