Skip to content

Commit

Permalink
feat: add inlayHints.typeHints
Browse files Browse the repository at this point in the history
  • Loading branch information
fannheyward committed Nov 30, 2020
1 parent 42109e3 commit eeda508
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 27 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ This extension is configured using a jsonc file. You can open this configuration
- `rust-analyzer.diagnostics.warningsAsInfo`: List of warnings that should be displayed with info severity, default: `[]`
- `rust-analyzer.diagnostics.warningsAsHint`: List of warnings that should be displayed with hint severity, default: `[]`
- `rust-analyzer.lruCapacity`: Number of syntax trees rust-analyzer keeps in memory, default: `null`
- `rust-analyzer.inlayHints.typeHints`: Whether to show inlay type hints for variables, **Neovim Only**, default `true`
- `rust-analyzer.inlayHints.typeHintsSeparator`: Separator text for typeHints in virtual text, default ``
- `rust-analyzer.inlayHints.chainingHints`: Whether to show inlay type hints for method chains, **Neovim Only**, default `true`
- `rust-analyzer.inlayHints.chainingHintsSeparator`: Separator text for chainingHints in virtual text, default ``
- `rust-analyzer.inlayHints.refreshOnInsertMode`: Whether to refresh inlayHints on insert mode, default `false`
Expand Down Expand Up @@ -86,6 +88,7 @@ You can use these commands by `:CocCommand XYZ`.

## Highlight Group

- `CocRustTypeHint`: highlight name for `typeHints`, default link to `CocHintSign`
- `CocRustChainingHint`: highlight name for `chainingHints`, default link to `CocHintSign`

## License
Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,16 @@
"default": null,
"description": "List of features to activate. Defaults to `rust-analyzer.cargo.features`."
},
"rust-analyzer.inlayHints.typeHints": {
"type": "boolean",
"default": true,
"description": "Whether to show inlay type hints for variables. *Neovim Only*"
},
"rust-analyzer.inlayHints.typeHintsSeparator": {
"type": "string",
"default": "",
"description": "Separator text for typeHints in virtual text"
},
"rust-analyzer.inlayHints.chainingHints": {
"type": "boolean",
"default": true,
Expand Down
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export class Config {
get inlayHints() {
const hasVirtualText = workspace.isNvim && workspace.nvim.hasFunction('nvim_buf_set_virtual_text');
return {
typeHints: hasVirtualText && this.cfg.get<boolean>('inlayHints.typeHints'),
typeHintsSeparator: this.cfg.get<string>('inlayHints.typeHintsSeparator'),
chainingHints: hasVirtualText && this.cfg.get<boolean>('inlayHints.chainingHints'),
chainingHintsSeparator: this.cfg.get<string>('inlayHints.chainingHintsSeparator'),
refreshOnInsertMode: hasVirtualText && this.cfg.get<boolean>('inlayHints.refreshOnInsertMode'),
Expand Down
66 changes: 39 additions & 27 deletions src/inlay_hints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ interface RustSourceFile {
class HintsUpdater implements Disposable {
private sourceFiles = new Map<string, RustSourceFile>(); // map Uri -> RustSourceFile
private readonly disposables: Disposable[] = [];
private chainingHintNS = workspace.createNameSpace('rust-chaining-hint');
private chainingHintEnabled = true;
private inlayHintsNS = workspace.createNameSpace('rust-inlay-hint');
private inlayHintsEnabled = true;

constructor(private readonly ctx: Ctx) {
// Set up initial cache shape
workspace.documents.forEach((doc) => {
if (doc && isRustDocument(doc.textDocument)) {
doc.buffer.clearNamespace(this.chainingHintNS);
doc.buffer.clearNamespace(this.inlayHintsNS);
this.sourceFiles.set(doc.uri, { document: doc.textDocument, inlaysRequest: null });
}
});

events.on('InsertLeave', async (bufnr) => {
const doc = workspace.getDocument(bufnr);
if (doc && isRustDocument(doc.textDocument)) {
doc.buffer.clearNamespace(this.chainingHintNS);
doc.buffer.clearNamespace(this.inlayHintsNS);
this.syncAndRenderHints();
}
});
Expand All @@ -45,7 +45,7 @@ class HintsUpdater implements Disposable {
(e) => {
const doc = workspace.getDocument(e.bufnr);
if (doc && isRustDocument(doc.textDocument)) {
doc.buffer.clearNamespace(this.chainingHintNS);
doc.buffer.clearNamespace(this.inlayHintsNS);
if (workspace.insertMode && !this.ctx.config.inlayHints.refreshOnInsertMode) {
return;
}
Expand All @@ -66,7 +66,7 @@ class HintsUpdater implements Disposable {
this.sourceFiles.set(e.uri, file);

const doc = workspace.getDocument(e.uri);
doc.buffer.clearNamespace(this.chainingHintNS);
doc.buffer.clearNamespace(this.inlayHintsNS);
this.syncAndRenderHints();
}
},
Expand All @@ -83,53 +83,64 @@ class HintsUpdater implements Disposable {
}

async toggle() {
if (this.chainingHintEnabled) {
this.chainingHintEnabled = false;
if (this.inlayHintsEnabled) {
this.inlayHintsEnabled = false;
this.dispose();

const doc = await workspace.document;
if (!doc) return;

doc.buffer.clearNamespace(this.chainingHintNS);
doc.buffer.clearNamespace(this.inlayHintsNS);
} else {
this.chainingHintEnabled = true;
this.inlayHintsEnabled = true;
this.syncAndRenderHints();
}
}

async syncAndRenderHints() {
if (!this.chainingHintEnabled) return;
if (!this.inlayHintsEnabled) return;
const current = await workspace.document;
this.sourceFiles.forEach((file, uri) =>
this.fetchHints(file).then(async (hints) => {
if (!hints) return;

if (current && current.uri === uri && isRustDocument(current.textDocument)) {
const decorations = this.hintsToDecorations(hints);
this.renderDecorations(current, decorations);
this.renderHints(current, hints);
}
})
);
}

private async renderDecorations(doc: Document, decorations: InlaysDecorations) {
doc.buffer.clearNamespace(this.chainingHintNS);
const sep = this.ctx.config.inlayHints.chainingHintsSeparator;
for (const item of decorations.chaining) {
doc.buffer.setVirtualText(this.chainingHintNS, item.range.end.line, [[`${sep}${item.label}`, 'CocRustChainingHint']], {}).logError();
}
}

private hintsToDecorations(hints: ra.InlayHint[]): InlaysDecorations {
private async renderHints(doc: Document, hints: ra.InlayHint[]) {
const decorations: InlaysDecorations = { type: [], param: [], chaining: [] };
for (const hint of hints) {
// ChainingHint only now
if (hint.kind === ra.InlayHint.Kind.ChainingHint) {
decorations.chaining.push(hint);
switch (hint.kind) {
case ra.InlayHint.Kind.TypeHint:
decorations.type.push(hint);
break;
case ra.InlayHint.Kind.ChainingHint:
decorations.chaining.push(hint);
break;
default:
continue;
}
}

return decorations;
doc.buffer.clearNamespace(this.inlayHintsNS);
if (this.ctx.config.inlayHints.chainingHints) {
const sep = this.ctx.config.inlayHints.chainingHintsSeparator;
for (const item of decorations.chaining) {
const chunks: [[string, string]] = [[`${sep}${item.label}`, 'CocRustChainingHint']];
doc.buffer.setVirtualText(this.inlayHintsNS, item.range.end.line, chunks, {}).logError();
}
}
if (this.ctx.config.inlayHints.typeHints) {
const sep = this.ctx.config.inlayHints.typeHintsSeparator;
for (const item of decorations.type) {
const chunks: [[string, string]] = [[`${sep}${item.label}`, 'CocRustTypeHint']];
doc.buffer.setVirtualText(this.inlayHintsNS, item.range.end.line, chunks, {}).logError();
}
}
}

private async fetchHints(file: RustSourceFile): Promise<null | ra.InlayHint[]> {
Expand All @@ -154,12 +165,13 @@ export function activateInlayHints(ctx: Ctx) {
const maybeUpdater = {
updater: null as null | HintsUpdater,
async onConfigChange() {
if (!ctx.config.inlayHints.chainingHints) {
if (!ctx.config.inlayHints.chainingHints && !ctx.config.inlayHints.typeHints) {
return this.dispose();
}

await ctx.sleep(100);
await workspace.nvim.command('hi default link CocRustChainingHint CocHintSign');
await workspace.nvim.command('hi default link CocRustTypeHint CocHintSign');
if (this.updater) {
this.updater.syncAndRenderHints();
} else {
Expand Down

0 comments on commit eeda508

Please sign in to comment.