缩进是代码可读性的基础。不同语言、不同团队可能采用不同的缩进策略,编辑器需要灵活支持这些配置,并提供良好的可视化体验。
缩进类型
常见的缩进类型有两种:
- 制表符(Tab):使用ASCII tab字符,可以自定义显示宽度
- 空格(Space):使用空格字符,通常是2或4个空格
缩进配置
编辑器需要支持灵活的缩进配置:
/* 缩进配置 */
interface IndentConfig {
useTab: boolean; /* true=Tab, false=空格 */
tabSize: number; /* Tab显示宽度 */
indentSize: number; /* 缩进宽度 */
autoIndent: boolean; /* 自动缩进 */
}
const defaultConfig: IndentConfig = {
useTab: false,
tabSize: 4,
indentSize: 4,
autoIndent: true
};
/* 检测文件使用的缩进类型 */
function detectIndent(text: string): IndentConfig {
const lines = text.split('\n');
let tabCount = 0;
let space2Count = 0;
let space4Count = 0;
for (const line of lines) {
/* 检查行首的缩进 */
const match = line.match(/^(\s+)/);
if (!match) continue;
const indent = match[1];
if (includes(indent, '\t')) tabCount++;
else if (indent.length % 2 === 0 && indent[0] === ' ') {
if (indent.length === 2) space2Count++;
else if (indent.length % 4 === 0) space4Count++;
}
}
/* 返回检测结果 */
if (tabCount > space2Count && tabCount > space4Count) {
return { useTab: true, tabSize: 4, indentSize: 4, autoIndent: true };
} else if (space4Count >= space2Count) {
return { useTab: false, tabSize: 4, indentSize: 4, autoIndent: true };
}
return defaultConfig;
}
Tab转换为空格
在编辑时处理Tab和空格的转换:
/* Tab转空格 */
function tabToSpaces(text: string, tabSize: number): string {
return text.replace(/\t/g, ' '.repeat(tabSize));
}
/* 空格转Tab */
function spacesToTab(text: string, tabSize: number): string {
let result = '';
let spaces = 0;
for (const char of text) {
if (char === ' ') {
spaces++;
if (spaces === tabSize) {
result += '\t';
spaces = 0;
}
} else {
if (spaces > 0) {
result += ' '.repeat(spaces);
spaces = 0;
}
result += char;
}
}
if (spaces > 0) {
result += ' '.repeat(spaces);
}
return result;
}
自动缩进
按Enter时自动继承或增加缩进:
/* 计算新行的缩进 */
function calculateIndent(
currentLine: string,
config: IndentConfig
): string {
/* 获取当前行的缩进 */
const match = currentLine.match(^(\s*));
let indent = match ? match[1] : '';
/* 如果是Tab模式,转换为Tab */
if (config.useTab) {
/* 检查是否需要增加缩进(大括号结尾) */
if (/[{[\(]$/.test(currentLine.trim())) {
const currentIndent = getIndentCount(indent, config);
indent = makeIndent(currentIndent + 1, config);
}
} else {
/* 空格模式 */
if (/[{[\(]$/.test(currentLine.trim())) {
const currentIndent = indent.length;
indent = ' '.repeat(currentIndent + config.indentSize);
}
}
return indent;
}
function makeIndent(level: number, config: IndentConfig): string {
if (config.useTab) {
return '\t'.repeat(level);
}
return ' '.repeat(level * config.indentSize);
}
function getIndentCount(indent: string, config: IndentConfig): number {
if (config.useTab) {
return (indent.match(/\t/g) || []).length;
}
return Math.floor(indent.length / config.indentSize);
}
可视化空白字符
让用户看到不可见字符:
/* 可视化渲染配置 */
interface WhitespaceRender {
showSpaces: boolean;
showTabs: boolean;
showLineEndings: boolean;
}
/* 空白字符渲染器 */
class WhitespaceRenderer {
render(text: string, render: WhitespaceRender): string {
let result = text;
if (render.showTabs) {
/* 将Tab渲染为箭头符号 */
result = result.replace(
/\t/g,
'→' .repeat(3)
);
}
if (render.showSpaces) {
/* 将尾部空格渲染为点 */
result = result.replace(
/ +$/gm,
(match) => '·'.repeat(match.length)
);
}
if (render.showLineEndings) {
/* 显示行尾符号 */
result = result.replace(
/\n/g,
'↵\n'
);
}
return result;
}
}
智能缩进检测
根据语言特性自动调整缩进:
/* 语言特定的缩进规则 */
const languageIndentRules = {
'python': {
useTab: false,
indentSize: 4,
getSmartIndent: (line) => {
/* Python: 缩进跟随上一行 */
const match = line.match(/^(\s*)/);
return match ? match[1].length : 0;
}
},
'javascript': {
useTab: false,
indentSize: 2,
getSmartIndent: (line) => {
/* JS: 大括号不增加缩进 */
const trimmed = line.trim();
const baseIndent = getBaseIndent(line);
if (/[{[\(]$/.test(trimmed)) {
return baseIndent + 2;
}
if (/[}\])]$/.test(trimmed)) {
return Math.max(0, baseIndent - 2);
}
return baseIndent;
}
}
};
用户偏好
提供可视化选项让用户选择是否显示空白字符,同时在状态栏显示当前的缩进类型。
总结
- 支持Tab和空格两种缩进类型
- 提供自动检测文件缩进类型的功能
- 自动缩进根据语言特性智能调整
- 可视化空白字符提升可读性
- 支持语言特定的缩进规则