coc-ccls
Version:
C/C++/ObjC language server supporting cross references, hierarchies, completion and semantic highlighting
209 lines (188 loc) • 6.66 kB
text/typescript
import {
workspace,
} from "coc.nvim";
import {
Disposable,
Range,
} from "vscode-languageserver-protocol";
import { SymbolKind } from "vscode-languageserver-types";
import { disposeAll, normalizeUri, unwrap } from "./utils";
const window = workspace;
enum CclsSymbolKind {
// ls.SymbolKind ccls extensions
TypeAlias = 252,
Parameter = 253,
StaticMethod = 254,
Macro = 255
}
enum StorageClass {
Invalid,
None,
Extern,
Static,
PrivateExtern,
Auto,
Register
}
interface SemanticSymbol {
readonly id: number;
readonly parentKind: SymbolKind;
readonly kind: SymbolKind;
readonly isTypeMember: boolean;
readonly storage: StorageClass;
readonly lsRanges: Range[];
}
export interface PublishSemanticHighlightArgs {
readonly uri: string;
readonly symbols: SemanticSymbol[];
}
// function makeSemanticDecorationType(
// color: string|undefined, underline: boolean, italic: boolean,
// bold: boolean): TextEditorDecorationType {
// const opts: DecorationRenderOptions = {};
// opts.rangeBehavior = DecorationRangeBehavior.ClosedClosed;
// opts.color = color;
// if (underline === true)
// opts.textDecoration = 'underline';
// if (italic === true)
// opts.fontStyle = 'italic';
// if (bold === true)
// opts.fontWeight = 'bold';
// return window.createTextEditorDecorationType(
// opts as DecorationRenderOptions);
// }
export const semanticTypes: {[name: string]: Array<SymbolKind|CclsSymbolKind>} = {
enumConstants: [SymbolKind.EnumMember],
enums: [SymbolKind.Enum],
freeStandingFunctions: [SymbolKind.Function],
freeStandingVariables: [],
globalVariables: [],
macros: [CclsSymbolKind.Macro],
memberFunctions: [SymbolKind.Method, SymbolKind.Constructor],
memberVariables: [SymbolKind.Field],
namespaces: [SymbolKind.Namespace],
parameters: [CclsSymbolKind.Parameter],
staticMemberFunctions: [CclsSymbolKind.StaticMethod],
staticMemberVariables: [],
templateParameters: [SymbolKind.TypeParameter],
typeAliases: [CclsSymbolKind.TypeAlias],
types: [SymbolKind.Class, SymbolKind.Struct],
};
// function makeDecorations(type: string) {
// const config = workspace.getConfiguration('ccls');
// let colors = config.get(`highlighting.colors.${type}`, [undefined]);
// if (colors.length === 0)
// colors = [undefined];
// const u = config.get(`highlighting.underline.${type}`, false);
// const i = config.get(`highlighting.italic.${type}`, false);
// const b = config.get(`highlighting.bold.${type}`, false);
// return colors.map((c) => makeSemanticDecorationType(c, u, i, b));
// }
// TODO: enable bold/italic decorators, might need change in vscode
export class SemanticContext implements Disposable {
// private semanticDecorations = new Map<string, TextEditorDecorationType[]>();
private semanticEnabled = new Map<string, boolean>();
// private cachedDecorations = new Map<string, Map<TextEditorDecorationType, Array<Range>>>();
private _dispose: Disposable[] = [];
public constructor() {
for (const type of Object.keys(semanticTypes)) {
// this.semanticDecorations.set(type, makeDecorations(type));
this.semanticEnabled.set(type, false);
}
this.updateConfigValues();
// workspace.onDidChangeActiveTextEditor(
// (editor?: TextEditor) => {
// if (editor) {
// this.updateDecoration(editor);
// }
// },
// undefined,
// this._dispose
// );
}
public dispose() {
disposeAll(this._dispose);
}
public publishSemanticHighlight(args: PublishSemanticHighlightArgs) {
this.updateConfigValues();
const normUri = normalizeUri(args.uri);
// for (const visibleEditor of window.visibleTextEditors) {
// if (normUri !== visibleEditor.document.uri.toString(true))
// continue;
//
// const decorations = new Map<TextEditorDecorationType, Array<Range>>();
//
// for (const symbol of args.symbols) {
// const type = this.tryFindDecoration(symbol);
// if (!type)
// continue;
// const existing = decorations.get(type);
// if (existing) {
// for (const range of symbol.lsRanges) {
// existing.push(range);
// }
// } else {
// decorations.set(type, symbol.lsRanges);
// }
// }
// TODO limit cache size
// this.cachedDecorations.set(normUri, decorations);
// this.updateDecoration(visibleEditor);
// }
}
private updateConfigValues() {
// Fetch new config instance, since vscode will cache the previous one.
const config = workspace.getConfiguration('ccls');
for (const [name, _value] of this.semanticEnabled) {
this.semanticEnabled.set(name, config.get(`highlighting.enabled.${name}`, false));
}
}
// private tryFindDecoration(
// symbol: SemanticSymbol
// ): TextEditorDecorationType|undefined {
// const get = (name: string) => {
// if (!this.semanticEnabled.get(name))
// return undefined;
// const decorations = unwrap(this.semanticDecorations.get(name), "semantic");
// return decorations[symbol.id % decorations.length];
// };
//
// if (symbol.kind === SymbolKind.Variable) {
// if (symbol.parentKind === SymbolKind.Function ||
// symbol.parentKind === SymbolKind.Method ||
// symbol.parentKind === SymbolKind.Constructor) {
// return get('freeStandingVariables');
// }
// return get('globalVariables');
// } else if (symbol.kind === SymbolKind.Field) {
// if (symbol.storage === StorageClass.Static) {
// return get('staticMemberVariables');
// }
// return get('memberVariables');
// } else {
// for (const name of Object.keys(semanticTypes)) {
// const kinds = semanticTypes[name];
// if (kinds.some((e) => e === symbol.kind)) {
// return get(name);
// }
// }
// }
// }
// private updateDecoration(editor: TextEditor) {
// const uri = editor.document.uri.toString(true);
// const cachedDecoration = this.cachedDecorations.get(uri);
// if (cachedDecoration) {
// // Clear decorations and set new ones. We might not use all of the
// // decorations so clear before setting.
// for (const [_, decorations] of this.semanticDecorations) {
// decorations.forEach((type) => {
// editor.setDecorations(type, []);
// });
// }
// // Set new decorations.
// cachedDecoration.forEach((ranges, type) => {
// editor.setDecorations(type, ranges);
// });
// }
// }
}