Skip to content

Conversation

lambdalisue
Copy link
Contributor

Summary

This PR adds 13 new builtin extensions to fulfill the missing extensions mentioned in #2, plus some additional useful ones for Vim/Neovim users.

New Extensions

Sources (11 new)

  • window - Lists all Vim windows with buffer information
  • tabpage - Lists all tab pages with window counts
  • loclist - Lists location list items (window-specific error lists)
  • colorscheme - Lists available colorschemes with current marker
  • highlight - Lists highlight groups with link information
  • jumplist - Lists jump locations from Vim's jumplist
  • register - Lists registers with their contents and types
  • mark - Lists marks (local, global, special) with locations
  • command - Lists user-defined commands with definitions
  • mapping - Lists key mappings across different modes
  • git_status - Lists modified files from git status (bonus)

Previewers (1 new)

  • shell - Executes shell commands and displays their output

Renderers (1 new)

  • file_info - Appends file metadata (size, date, permissions) to items

Implementation Details

All extensions:

  • Follow the established patterns in the codebase
  • Use TypeScript with proper type safety
  • Support configurable options
  • Handle errors gracefully
  • Are properly formatted with deno fmt
  • Pass type checking with deno task check
  • Are exported through the generated mod.ts files

Each extension was committed individually for easy review and potential cherry-picking.

Related Issues

Closes #2

Test plan

  • Each extension has been manually tested in a development environment
  • Type checking passes (deno task check)
  • Code is properly formatted (deno fmt)

🤖 Generated with Claude Code

Copy link

codecov bot commented Jul 6, 2025

Codecov Report

❌ Patch coverage is 9.06801% with 1444 lines in your changes missing coverage. Please review.
✅ Project coverage is 34.22%. Comparing base (1f3ebe1) to head (c166b92).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
builtin/renderer/file_info.ts 0.74% 133 Missing ⚠️
builtin/renderer/smart_grep.ts 1.86% 105 Missing ⚠️
builtin/renderer/buffer_info.ts 1.98% 99 Missing ⚠️
builtin/source/git_status.ts 15.51% 98 Missing ⚠️
builtin/source/vimgrep.ts 2.10% 93 Missing ⚠️
builtin/source/grep.ts 2.17% 90 Missing ⚠️
builtin/source/command.ts 2.29% 85 Missing ⚠️
builtin/refiner/buffer_info.ts 2.32% 84 Missing ⚠️
builtin/refiner/file_info.ts 2.32% 84 Missing ⚠️
builtin/previewer/shell.ts 3.79% 76 Missing ⚠️
... and 10 more

❗ There is a different number of reports uploaded between BASE (1f3ebe1) and HEAD (c166b92). Click for more details.

HEAD has 2 uploads less than BASE
Flag BASE (1f3ebe1) HEAD (c166b92)
5 3
Additional details and impacted files
@@             Coverage Diff             @@
##             main       #9       +/-   ##
===========================================
- Coverage   51.46%   34.22%   -17.24%     
===========================================
  Files          78       98       +20     
  Lines        2318     3906     +1588     
  Branches       70       70               
===========================================
+ Hits         1193     1337      +144     
- Misses       1125     2569     +1444     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@lambdalisue lambdalisue marked this pull request as draft July 6, 2025 02:19
- Note that if you use `@denops/std/function`, some functions already provides
proper types so you may not need to use `@core/unknownutil`
- You should try hard to avoid using `as any` or `as unkonwn as`. Proper
type-guarding is preferred.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All changes applied to this file should be commited in the previous commit.

Comment on lines +75 to +93
if (showLabel) {
// Try to get tab label from 't:tabLabel' variable
const customLabel = await fn.gettabvar(denops, tabnr, "tabLabel") as
| string
| null;
if (customLabel) {
label = customLabel;
} else {
// Create label from buffer names in the tab
const bufferNames = [];
for (const w of windows) {
const name = await fn.bufname(denops, w.bufnr);
bufferNames.push(
name ? name.split("/").pop() || name : "[No Name]",
);
}
label = bufferNames.join(", ");
}
} else {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think Vim nor Neovim provides such variable (t:tabLabel). List up the reference that mentioned that variable or remove this custom label feature.

Comment on lines +102 to +104
const value = `${prefix}${tabnr}: ${label} (${windowCount} window${
windowCount !== 1 ? "s" : ""
})`;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

tabnr is repeated if the label is Tab ${tabnr}.

denops,
"",
"color",
) as string[];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we really need this as string[] annotation?

// Get current colorscheme if needed
let currentColorscheme = "";
if (markCurrent) {
const colors = await fn.execute(denops, "colorscheme") as string;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's better to use @core/unknownutil to check if the value is really string or not.

Comment on lines +161 to +172
items.push({
id: id++,
value: `:${cmd} [builtin]`,
detail: {
name: cmd,
definition: "(builtin command)",
attributes: "",
bufferLocal: false,
nargs: "?",
complete: undefined,
},
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just yield.

Comment on lines +94 to +106
const mappingList = await fn.maplist(denops, mode) as Array<{
lhs: string;
rhs: string;
silent: number | boolean;
noremap: number | boolean;
nowait: number | boolean;
expr: number | boolean;
buffer: number | boolean;
mode?: string;
sid?: number;
lnum?: number;
script?: number | boolean;
}>;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Avoid using as. Try @core/unknownutil to use type guard.

Comment on lines +157 to +162
// Sort by mode and then by lhs
items.sort((a, b) => {
const modeCmp = a.detail.mode.localeCompare(b.detail.mode);
if (modeCmp !== 0) return modeCmp;
return a.detail.lhs.localeCompare(b.detail.lhs);
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should NOT sort in source.

Comment on lines +140 to +153
items.push({
id: id++,
value: `${modeIndicator} ${mapping.lhs}${attrStr} → ${truncatedRhs}`,
detail: {
lhs: mapping.lhs,
rhs: mapping.rhs,
mode: mode,
bufferLocal: isBufferLocal,
silent: Boolean(mapping.silent),
noremap: Boolean(mapping.noremap),
nowait: Boolean(mapping.nowait),
expr: Boolean(mapping.expr),
},
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just yield.

Comment on lines +137 to +183
// Git status format: XY filename
// X = staged status, Y = unstaged status
const staged = line[0];
const unstaged = line[1];
const filename = line.substring(3);

// Determine status code and description
const status = `${staged}${unstaged}`;
let statusDescription = "";
let isStaged = false;
let isUnstaged = false;

// Parse status codes
if (status === STATUS_CODES.UNTRACKED) {
statusDescription = "untracked";
isUnstaged = true;
} else if (status === STATUS_CODES.IGNORED) {
statusDescription = "ignored";
} else {
// Handle staged status
const stagedDesc =
STATUS_CODES.STAGED[staged as keyof typeof STATUS_CODES.STAGED];
if (stagedDesc) {
statusDescription = stagedDesc;
isStaged = true;
}

// Handle unstaged status
const unstagedDesc = STATUS_CODES
.UNSTAGED[unstaged as keyof typeof STATUS_CODES.UNSTAGED];
if (unstagedDesc) {
statusDescription += isStaged ? `, ${unstagedDesc}` : unstagedDesc;
isUnstaged = true;
}
}

// Create status indicator
const indicator = status === STATUS_CODES.UNTRACKED
? "[?]"
: status === STATUS_CODES.IGNORED
? "[!]"
: `[${status}]`;

// Format display value
const absolutePath = join(cwd, filename);
const displayPath = filename;
const value = `${indicator.padEnd(5)} ${displayPath}`;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add a parser as a pure function so that we can easily test.

@lambdalisue lambdalisue closed this Aug 2, 2025
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add more builtin extensions
1 participant