diff --git a/package.json b/package.json index 5bc16bb..4a2e42f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "bugs": { "url": "https://github.com/pragmagic/vscode-nim/issues" - }, + }, "scripts": { "vscode:prepublish": "tsc -p ./", "compile": "tsc -watch -p ./", @@ -144,6 +144,66 @@ "type": "string", "default": "", "description": "Optional license text that will be inserted on nim file creation." + }, + "nim.typeCasing": { + "type": "string", + "default": "as-is", + "description": "The casing of types and type aliases", + "enum": [ + "as-is", + "pascal-camel", + "snake" + ] + }, + "nim.moduleCasing": { + "type": "string", + "default": "as-is", + "description": "The casing of modules", + "enum": [ + "as-is", + "pascal-camel", + "snake" + ] + }, + "nim.procLikeCasing": { + "type": "string", + "default": "as-is", + "description": "The casing of procs, templates, macros and iterators", + "enum": [ + "as-is", + "pascal-camel", + "snake" + ] + }, + "nim.constantCasing": { + "type": "string", + "default": "as-is", + "description": "The casing of constants", + "enum": [ + "as-is", + "pascal-camel", + "snake" + ] + }, + "nim.enumFieldCasing": { + "type": "string", + "default": "as-is", + "description": "The casing of enum fields", + "enum": [ + "as-is", + "pascal-camel", + "snake" + ] + }, + "nim.variableCasing": { + "type": "string", + "default": "as-is", + "description": "The casing of var, let, parameters, result and object/tuple fields", + "enum": [ + "as-is", + "pascal-camel", + "snake" + ] } } }, diff --git a/src/nimCasing.ts b/src/nimCasing.ts new file mode 100644 index 0000000..8cefdcb --- /dev/null +++ b/src/nimCasing.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------- + * Copyright (C) Xored Software Inc., RSDuck All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *--------------------------------------------------------*/ + +import vscode = require('vscode'); + +function casingSplit(sym: string): [boolean, string[]] { + if (!sym.match(/\w+/)) return [false, [sym]]; // exclude operators + if (sym === sym.toUpperCase()) sym = sym.toLowerCase(); // for C_IDENTIFIERS_LIKE_SO + let start = 0; + let capital = sym[0].toUpperCase() === sym[0]; + let result: string[] = []; + for (let i = 0; i < sym.length; i++) { + if (i > 0 && ((sym[i].toUpperCase() === sym[i] && sym[i] !== '_') || sym[i - 1] === '_')) { + let wasUnderscore = sym[i - 1] === '_' ? 1 : 0; + result.push(sym.substring(start, i - wasUnderscore).toLowerCase()); + start = i; + } + } + result.push(sym.substring(start, sym.length).toLowerCase()); + if (result[result.length - 1] === '=') { + result.pop(); + result[result.length - 1] = result[result.length - 1] + '='; + } + return [capital, result]; +} + +function toCamelPascalCase(sym: string) { + let parts = casingSplit(sym); + if (parts[0]) { + let assembled = ''; + for (let i = 0; i < parts[1].length; i++) { + assembled += parts[1][i][0].toUpperCase() + parts[1][i].substring(1, parts[1][i].length); + } + return assembled; + } else { + let assembled = parts[1][0]; + for (let i = 1; i < parts[1].length; i++) + assembled += parts[1][i][0].toUpperCase() + parts[1][i].substring(1, parts[1][i].length); + return assembled; + } +} + +function toSnakeCase(sym: string): string { + let parts = casingSplit(sym); + if (parts[0]) { + let assembled = parts[1][0][0].toUpperCase() + parts[1][0].substring(1, parts[1][0].length); + for (let i = 1; i < parts[1].length; i++) + assembled += '_' + parts[1][i][0].toUpperCase() + parts[1][i].substring(1, parts[1][i].length); + return assembled; + } else { + let assembled = parts[1][0]; + for (let i = 1; i < parts[1].length; i++) { + assembled += '_' + parts[1][i]; + } + return assembled; + } +} + +function keepCasing(sym: string): string { return sym; } + +let + nimCasingConfig = new Map string>(); + +export function configureCasing(config: vscode.WorkspaceConfiguration) { + function toFunction(mode: string): (sym: string) => string { + switch (mode) { + case 'as-is': + return keepCasing; + case 'pascal-camel': + return toCamelPascalCase; + case 'snake': + return toSnakeCase; + } + } + + let typeCasing = toFunction(config['typeCasing']); + let moduleCasing = toFunction(config['moduleCasing']); + let procLikeCasing = toFunction(config['procLikeCasing']); + let constantCasing = toFunction(config['constantCasing']); + let enumFieldCasing = toFunction(config['enumFieldCasing']); + let variableCasing = toFunction(config['variableCasing']); + + nimCasingConfig['skConst'] = constantCasing; + nimCasingConfig['skEnumField'] = enumFieldCasing; + nimCasingConfig['skForVar'] = variableCasing; + nimCasingConfig['skIterator'] = procLikeCasing; + nimCasingConfig['skLabel'] = variableCasing; + nimCasingConfig['skLet'] = variableCasing; + nimCasingConfig['skMacro'] = procLikeCasing; + nimCasingConfig['skMethod'] = procLikeCasing; + nimCasingConfig['skParam'] = variableCasing; + nimCasingConfig['skProc'] = procLikeCasing; + nimCasingConfig['skResult'] = variableCasing; + nimCasingConfig['skTemplate'] = procLikeCasing; + nimCasingConfig['skType'] = typeCasing; + nimCasingConfig['skVar'] = variableCasing; + nimCasingConfig['skField'] = variableCasing; + nimCasingConfig['skAlias'] = typeCasing; + nimCasingConfig['skModule'] = moduleCasing; +} + +export function getCasingConfig(): Map string> { + return nimCasingConfig; +} \ No newline at end of file diff --git a/src/nimSuggestExec.ts b/src/nimSuggestExec.ts index 0ee8c53..1c477f8 100644 --- a/src/nimSuggestExec.ts +++ b/src/nimSuggestExec.ts @@ -15,6 +15,7 @@ import elrpc = require('./elrpc/elrpc'); import sexp = require('./elrpc/sexp'); import { prepareConfig, getProjectFile, isProjectMode, getNimExecPath, removeDirSync, correctBinname } from './nimUtils'; import { hideNimStatus, showNimStatus } from './nimStatus'; +import { getCasingConfig } from './nimCasing'; class NimSuggestProcessDescription { process: cp.ChildProcess; @@ -189,6 +190,8 @@ export async function execNimSuggest(suggestType: NimSuggestType, filename: stri let ret = await desc.rpc.callMethod(suggestCmd, { kind: 'string', str: normalizedFilename }, { kind: 'number', n: line }, { kind: 'number', n: column }, { kind: 'string', str: dirtyFile }); trace(desc.process.pid, projectFile + '=' + suggestCmd + ' ' + normalizedFilename, ret); + let casingConfig = getCasingConfig(); + var result: NimSuggestResult[] = []; if (ret != null) { if (ret instanceof Array) { @@ -198,7 +201,14 @@ export async function execNimSuggest(suggestType: NimSuggestType, filename: stri var item = new NimSuggestResult(); item.answerType = parts[0]; item.suggest = parts[1]; - item.names = parts[2]; + item.names = []; + + if (parts[2].length > 1) + item.names.push(casingConfig['skModule'](parts[2][0])); + + for (let i = 1; i < parts[2].length; i++) + item.names.push(casingConfig[item.suggest](parts[2][i])); + item.path = parts[3].replace(/\\,\\/g, '\\'); item.type = parts[4]; item.line = parts[5]; diff --git a/src/nimUtils.ts b/src/nimUtils.ts index 89c6b06..01bce0f 100644 --- a/src/nimUtils.ts +++ b/src/nimUtils.ts @@ -11,6 +11,7 @@ import os = require('os'); import cp = require('child_process'); import vscode = require('vscode'); import { showNimStatus, hideNimStatus } from './nimStatus'; +import { configureCasing } from './nimCasing'; let _pathesCache: { [tool: string]: string; } = {}; var _projects: string[] = []; @@ -66,6 +67,7 @@ export function prepareConfig(): void { _projects.push(path.isAbsolute(projects) ? projects : path.resolve(vscode.workspace.rootPath, projects)); } } + configureCasing(config); } export function getBinPath(tool: string): string {