Skip to content

feat: add configurable file metadata column#54

Open
Goutham-AR wants to merge 9 commits intocorwinm:mainfrom
Goutham-AR:feat/add-metadata-columns
Open

feat: add configurable file metadata column#54
Goutham-AR wants to merge 9 commits intocorwinm:mainfrom
Goutham-AR:feat/add-metadata-columns

Conversation

@Goutham-AR
Copy link

feat: add configurable file metadata column

📝 Description

Adds opt-in file metadata columns to the oil directory listing, displayed as
virtual text decorations to the left of each filename — similar to Emacs Dired
and oil.nvim's column system.

The buffer text format (/NNN filename) is never modified, so the
save/diff pipeline is completely unaffected. Metadata is rendered purely via
VSCode before: decorations and never written into the document.

New setting oil-code.columns (default ["icon"] — no breaking change):

"oil-code.columns": ["icon", "permissions", "size", "mtime"]
Column Example Fixed width
icon 📄 existing
permissions -rw-r--r-- 10
size 12K 4, right-aligned, NBSP-padded
mtime Mar 14 14:23 12

New command oil-code.toggleDetails — toggles metadata display on/off for
the current session without changing the setting.

Binding Key
Native / non-Vim Alt+Shift+D
VSCodeVim normal mode <C-d>
neovim oil filetype <C-d>

🔧 Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • ♻️ Code refactoring (no functional changes)
  • ⚡ Performance improvement
  • 🧪 Tests (adding missing tests or correcting existing tests)
  • 🎨 Style changes (formatting, missing semi-colons, etc)
  • 🔧 Chore (maintenance tasks, dependency updates)
  • 🌟 Other (please describe):

🔗 Related Issues

Testing

  • I have tested this change locally
  • I have added/updated unit tests
  • All existing tests pass

Screenshots (if applicable)

Screenshot 2026-03-21 at 8 07 14 PM

Checklist

  • My code follows the project's coding standards
  • I have performed a self-review of my code
  • I have commented my code in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have checked my code and corrected any misspellings

Additional Notes

None

- Add MetadataColumn type and FileMetadata interface to constants.ts.
- Extend OilState with a metadataCache field.
Add metadataUtils.ts with fixed-width formatting helpers (formatSize,
formatMtime, formatPermissions, formatMetadataColumns) and
populateMetadataCache.
- Add getColumnsSettings() to settings.ts.
- Add peekOilState() to oilState.ts for use in the decoration layer.
- Initialize metadataCache in both OilState init functions.
- Call populateMetadataCache at the end of getDirectoryListing when at
least one non-icon column is configured. Clear the cache entry for the
current directory on refresh, alongside visitedPaths.

- Add oil-code.columns setting (array, default ["icon"]) with enum values
icon/permissions/size/mtime. Register oil-code.toggleDetails command
and alt+shift+d keybinding.
…mand

Add columnState.ts with session-level getDetailsVisible/toggleDetailsVisible.

- Add toggleDetails command that flips the flag and redraws all visible oil editors.

- Update updateDecorations to inject metadata as before: virtual text at the
filename start position when non-icon columns are configured and details are
visible. Metadata is never written to buffer text, so the /NNN filename format
and the save/diff pipeline are entirely unaffected.

- Register oil-code.toggleDetails in extension.ts.
- Map <C-d> to oil-code.toggleDetails in both the neovim Lua autocmd
block and the VSCodeVim normal-mode keymap registration.

- Add three tests: toggleDetails executes without error, rename with
metadata columns enabled does not corrupt file operations, and buffer
text always stays in /NNN filename format regardless of columns setting.
Copy link
Owner

@corwinm corwinm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really great so far, thank you! I added some comments for some improvements and I want to align the default keymaps so they don't conflict with the existing "" keymap.
Once those keymap changes are in place, we should update the README.md and help.ts content to include the new keymaps.
Thanks again! Overall this is really great work 😃

case "mtime":
parts.push(meta.mtime); // always 12 chars
break;
// "icon" is not a metadata column — silently ignored
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I will want to consolidate the two but that might have to be a follow up refactor unless you want to make that update.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, Could you clarify what you mean by "consolidate the two"? Happy to do this now, I just want to make sure I target the right thing.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean to consolidate the column configuration into one. Right now we have icons as its own unique thing and metadata as another. They should be consolidated to simplify the code. I haven't looked at this too deeply so I don't know if their is a downside or any breaking changes that would need to be considered to do this but that would clean things up if icons and the other columns were configured the same way oil.nvim has them configured.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unifying the rendering implementations for metadata and icons is not trivial due to the below reasons:

  • Icons need per-icon colors, In VSCode, it is set at TextEditorDecorationType creation time and cannot vary per range. This means we need one TextEditorDecorationType per icon key, stored in a Map.

Metadata is all the same muted color, so it only needs a single TextEditorDecorationType with dynamic contentText per line via renderOptions.before.

Happy to get your input on whether you'd prefer a different approach here.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can leave it for now and just acknowledge the limitation with how it is implemented. I think the implementation still fits the most common use case and we don't need to make it more complicated.

@corwinm
Copy link
Owner

corwinm commented Mar 22, 2026

Looks like their is still a problem where I see the icons rendered out of order.

image

This might be fixable by consolidating the icons and metadata columns into the same display logic but if you want to try this patch it appears to be working for me:

diff --git a/src/decorations.ts b/src/decorations.ts
index 6a6b13a..9d2b82d 100644
--- a/src/decorations.ts
+++ b/src/decorations.ts
@@ -119,14 +119,17 @@ const fileIconDecorations = new Map<string, vscode.TextEditorDecorationType>();
 // Single decoration type for all metadata before-text (style only; contentText is per-instance).
 // Applied AFTER icon decorations so VSCode stacks it between the icon and the filename text.
 let metadataDecorationType: vscode.TextEditorDecorationType | null = null;
-function getMetadataDecorationType(): vscode.TextEditorDecorationType {
-  if (!metadataDecorationType) {
-    metadataDecorationType = vscode.window.createTextEditorDecorationType({
-      before: {
-        color: new vscode.ThemeColor("editorInlayHint.foreground"),
-      },
-    });
+function recreateMetadataDecorationType(): vscode.TextEditorDecorationType {
+  if (metadataDecorationType) {
+    metadataDecorationType.dispose();
   }
+
+  metadataDecorationType = vscode.window.createTextEditorDecorationType({
+    before: {
+      color: new vscode.ThemeColor("editorInlayHint.foreground"),
+    },
+  });
+
   return metadataDecorationType;
 }
 
@@ -144,11 +147,6 @@ export function updateDecorations(editor: vscode.TextEditor | undefined) {
     editor.setDecorations(decoration, []);
   });
 
-  // Clear previous metadata decorations (avoid creating the decoration type too early)
-  if (metadataDecorationType) {
-    editor.setDecorations(metadataDecorationType, []);
-  }
-
   // Track icon decorations for this update
   const iconDecorations = new Map<string, vscode.Range[]>();
 
@@ -289,8 +287,8 @@ export function updateDecorations(editor: vscode.TextEditor | undefined) {
     }
   }
 
-  // Apply metadata after-decorations
-  editor.setDecorations(getMetadataDecorationType(), metaDecorations);
+  // Recreate metadata decoration type after icon types are prepared so ordering stays stable.
+  editor.setDecorations(recreateMetadataDecorationType(), metaDecorations);
 }
 
 // Disposable for cleanup
@@ -410,6 +408,10 @@ export function activateDecorations(context: vscode.ExtensionContext) {
       hiddenPrefixDecoration.dispose();
       fileIconDecorations.forEach((decoration) => decoration.dispose());
       fileIconDecorations.clear();
+      if (metadataDecorationType) {
+        metadataDecorationType.dispose();
+        metadataDecorationType = null;
+      }
 
       if (decorationUpdateListener) {
         decorationUpdateListener.dispose();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Configurable file metadata columns in directory listing

2 participants