编辑器 API
编辑器 API 提供了对 Trae IDE 文本编辑器的完整控制,包括文档操作、选择管理、装饰、代码片段等功能。
核心接口
TextEditor
文本编辑器是编辑器 API 的核心接口,代表一个打开的文本文档的编辑器视图。
typescript
interface TextEditor {
// 关联的文档
readonly document: TextDocument;
// 当前选择
selection: Selection;
selections: readonly Selection[];
// 可见范围
readonly visibleRanges: readonly Range[];
// 编辑器选项
options: TextEditorOptions;
// 视图列
readonly viewColumn?: ViewColumn;
}基本使用
typescript
// 获取当前活动编辑器
const editor = TraeAPI.window.activeTextEditor;
if (editor) {
console.log('当前文档:', editor.document.fileName);
console.log('当前选择:', editor.selection);
console.log('语言:', editor.document.languageId);
}
// 监听活动编辑器变化
TraeAPI.window.onDidChangeActiveTextEditor(editor => {
if (editor) {
console.log('切换到编辑器:', editor.document.fileName);
} else {
console.log('没有活动编辑器');
}
});TextDocument
文本文档表示编辑器中的文件内容。
typescript
interface TextDocument {
// 文档标识
readonly uri: Uri;
readonly fileName: string;
readonly languageId: string;
// 文档内容
readonly lineCount: number;
readonly version: number;
readonly isDirty: boolean;
readonly isClosed: boolean;
// 保存状态
readonly isUntitled: boolean;
// 文档操作方法
getText(range?: Range): string;
getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined;
lineAt(line: number | Position): TextLine;
offsetAt(position: Position): number;
positionAt(offset: number): Position;
validateRange(range: Range): Range;
validatePosition(position: Position): Position;
}文档操作示例
typescript
// 读取文档内容
const document = editor.document;
const fullText = document.getText();
const firstLine = document.lineAt(0).text;
const selection = document.getText(editor.selection);
// 获取单词范围
const position = editor.selection.active;
const wordRange = document.getWordRangeAtPosition(position);
if (wordRange) {
const word = document.getText(wordRange);
console.log('当前单词:', word);
}
// 位置和偏移量转换
const position = new TraeAPI.Position(10, 5);
const offset = document.offsetAt(position);
const backToPosition = document.positionAt(offset);文本操作
编辑操作
typescript
// 基本编辑
editor.edit(editBuilder => {
// 插入文本
editBuilder.insert(new TraeAPI.Position(0, 0), 'Hello, World!\n');
// 替换文本
const range = new TraeAPI.Range(1, 0, 1, 10);
editBuilder.replace(range, '新文本');
// 删除文本
const deleteRange = new TraeAPI.Range(2, 0, 3, 0);
editBuilder.delete(deleteRange);
});
// 带选项的编辑
editor.edit(editBuilder => {
editBuilder.insert(editor.selection.active, '插入的文本');
}, {
undoStopBefore: true,
undoStopAfter: true
});批量编辑
typescript
// 工作区编辑
const workspaceEdit = new TraeAPI.WorkspaceEdit();
// 编辑多个文件
workspaceEdit.replace(
document.uri,
new TraeAPI.Range(0, 0, 0, 5),
'替换文本'
);
workspaceEdit.insert(
document.uri,
new TraeAPI.Position(1, 0),
'插入文本\n'
);
// 应用编辑
const success = await TraeAPI.workspace.applyEdit(workspaceEdit);
if (success) {
console.log('编辑应用成功');
}格式化
typescript
// 格式化整个文档
const formatEdits = await TraeAPI.commands.executeCommand(
'editor.action.formatDocument'
);
// 格式化选择
const formatSelectionEdits = await TraeAPI.commands.executeCommand(
'editor.action.formatSelection'
);
// 自定义格式化
const formatProvider: TraeAPI.DocumentFormattingEditProvider = {
provideDocumentFormattingEdits(document, options, token) {
const edits: TraeAPI.TextEdit[] = [];
// 添加格式化逻辑
for (let i = 0; i < document.lineCount; i++) {
const line = document.lineAt(i);
if (line.text.startsWith(' ')) {
// 将双空格替换为制表符
edits.push(TraeAPI.TextEdit.replace(
new TraeAPI.Range(i, 0, i, 2),
'\t'
));
}
}
return edits;
}
};
// 注册格式化提供程序
TraeAPI.languages.registerDocumentFormattingEditProvider(
'typescript',
formatProvider
);选择与光标
Selection 和 Range
typescript
// 创建选择
const start = new TraeAPI.Position(0, 0);
const end = new TraeAPI.Position(0, 10);
const selection = new TraeAPI.Selection(start, end);
// 设置选择
editor.selection = selection;
// 多选择
editor.selections = [
new TraeAPI.Selection(0, 0, 0, 5),
new TraeAPI.Selection(1, 0, 1, 5),
new TraeAPI.Selection(2, 0, 2, 5)
];
// 选择操作
const currentSelection = editor.selection;
console.log('选择是否为空:', currentSelection.isEmpty);
console.log('选择是否为单个光标:', currentSelection.isSingleLine);
console.log('活动位置:', currentSelection.active);
console.log('锚点位置:', currentSelection.anchor);光标操作
typescript
// 移动光标
const moveToPosition = new TraeAPI.Position(10, 0);
editor.selection = new TraeAPI.Selection(moveToPosition, moveToPosition);
// 选择到行尾
const currentPos = editor.selection.active;
const lineEnd = editor.document.lineAt(currentPos.line).range.end;
editor.selection = new TraeAPI.Selection(currentPos, lineEnd);
// 选择整行
function selectLine(lineNumber: number) {
const line = editor.document.lineAt(lineNumber);
editor.selection = new TraeAPI.Selection(line.range.start, line.range.end);
}
// 扩展选择
function expandSelection() {
const currentSelection = editor.selection;
const document = editor.document;
// 扩展到单词边界
const wordRange = document.getWordRangeAtPosition(currentSelection.active);
if (wordRange) {
editor.selection = new TraeAPI.Selection(wordRange.start, wordRange.end);
}
}多光标操作
typescript
// 添加多个光标
function addCursorsAtPositions(positions: TraeAPI.Position[]) {
const selections = positions.map(pos =>
new TraeAPI.Selection(pos, pos)
);
editor.selections = selections;
}
// 在每行末尾添加光标
function addCursorsAtLineEnds() {
const selections: TraeAPI.Selection[] = [];
for (let i = 0; i < editor.document.lineCount; i++) {
const line = editor.document.lineAt(i);
const endPos = line.range.end;
selections.push(new TraeAPI.Selection(endPos, endPos));
}
editor.selections = selections;
}
// 查找并选择所有匹配项
function selectAllOccurrences(searchText: string) {
const document = editor.document;
const text = document.getText();
const selections: TraeAPI.Selection[] = [];
let index = 0;
while ((index = text.indexOf(searchText, index)) !== -1) {
const startPos = document.positionAt(index);
const endPos = document.positionAt(index + searchText.length);
selections.push(new TraeAPI.Selection(startPos, endPos));
index += searchText.length;
}
editor.selections = selections;
}装饰
文本装饰
typescript
// 创建装饰类型
const errorDecorationType = TraeAPI.window.createTextEditorDecorationType({
backgroundColor: 'rgba(255, 0, 0, 0.1)',
border: '1px solid red',
borderRadius: '3px',
overviewRulerColor: 'red',
overviewRulerLane: TraeAPI.OverviewRulerLane.Right
});
const warningDecorationType = TraeAPI.window.createTextEditorDecorationType({
backgroundColor: 'rgba(255, 255, 0, 0.1)',
border: '1px solid orange',
gutterIconPath: TraeAPI.Uri.file('/path/to/warning-icon.svg'),
gutterIconSize: 'contain'
});
// 应用装饰
function highlightErrors(ranges: TraeAPI.Range[]) {
const decorations: TraeAPI.DecorationOptions[] = ranges.map(range => ({
range,
hoverMessage: '发现错误'
}));
editor.setDecorations(errorDecorationType, decorations);
}
// 带详细信息的装饰
function highlightWithDetails(items: { range: TraeAPI.Range; message: string }[]) {
const decorations: TraeAPI.DecorationOptions[] = items.map(item => ({
range: item.range,
hoverMessage: new TraeAPI.MarkdownString(item.message),
renderOptions: {
after: {
contentText: ' ⚠️',
color: 'orange'
}
}
}));
editor.setDecorations(warningDecorationType, decorations);
}高级装饰
typescript
// 创建复杂装饰
const complexDecorationType = TraeAPI.window.createTextEditorDecorationType({
// 文本样式
color: '#ff0000',
fontWeight: 'bold',
fontStyle: 'italic',
textDecoration: 'underline',
// 背景和边框
backgroundColor: 'rgba(255, 0, 0, 0.1)',
border: '2px solid red',
borderRadius: '5px',
// 概览标尺
overviewRulerColor: 'red',
overviewRulerLane: TraeAPI.OverviewRulerLane.Full,
// 装订线图标
gutterIconPath: TraeAPI.Uri.file('/path/to/icon.svg'),
gutterIconSize: 'auto',
// 伪元素
before: {
contentText: '🔥',
margin: '0 5px 0 0'
},
after: {
contentText: ' (重要)',
color: 'gray',
fontStyle: 'italic'
}
});
// 动态装饰
class DiagnosticDecorator {
private decorationType: TraeAPI.TextEditorDecorationType;
constructor() {
this.decorationType = TraeAPI.window.createTextEditorDecorationType({
backgroundColor: 'rgba(255, 0, 0, 0.1)',
border: '1px solid red'
});
}
updateDecorations(editor: TraeAPI.TextEditor, diagnostics: TraeAPI.Diagnostic[]) {
const decorations: TraeAPI.DecorationOptions[] = diagnostics.map(diagnostic => ({
range: diagnostic.range,
hoverMessage: `${diagnostic.severity}: ${diagnostic.message}`,
renderOptions: {
after: {
contentText: ` (${diagnostic.code})`,
color: 'gray'
}
}
}));
editor.setDecorations(this.decorationType, decorations);
}
dispose() {
this.decorationType.dispose();
}
}代码片段
基本代码片段
typescript
// 插入简单代码片段
const snippet = new TraeAPI.SnippetString('console.log("${1:message}");');
editor.insertSnippet(snippet);
// 带多个占位符的代码片段
const functionSnippet = new TraeAPI.SnippetString([
'function ${1:functionName}(${2:parameters}) {',
'\t${3:// 函数体}',
'\treturn ${4:result};',
'}'
].join('\n'));
editor.insertSnippet(functionSnippet, editor.selection.active);高级代码片段
typescript
// React 组件代码片段
const reactComponentSnippet = new TraeAPI.SnippetString([
'import React from \'react\';',
'',
'interface ${1:ComponentName}Props {',
'\t${2:// props}',
'}',
'',
'const ${1:ComponentName}: React.FC<${1:ComponentName}Props> = (${3:props}) => {',
'\treturn (',
'\t\t<div>',
'\t\t\t${4:// JSX content}',
'\t\t</div>',
'\t);',
'};',
'',
'export default ${1:ComponentName};'
].join('\n'));
// 条件代码片段
function insertConditionalSnippet(condition: string) {
let snippet: TraeAPI.SnippetString;
if (condition === 'if') {
snippet = new TraeAPI.SnippetString([
'if (${1:condition}) {',
'\t${2:// 代码}',
'}'
].join('\n'));
} else if (condition === 'for') {
snippet = new TraeAPI.SnippetString([
'for (let ${1:i} = 0; ${1:i} < ${2:length}; ${1:i}++) {',
'\t${3:// 循环体}',
'}'
].join('\n'));
} else {
snippet = new TraeAPI.SnippetString('${1:// 代码}');
}
editor.insertSnippet(snippet);
}
// 动态代码片段
class SnippetProvider {
private snippets = new Map<string, TraeAPI.SnippetString>();
constructor() {
this.initializeSnippets();
}
private initializeSnippets() {
// 类代码片段
this.snippets.set('class', new TraeAPI.SnippetString([
'class ${1:ClassName} {',
'\tconstructor(${2:parameters}) {',
'\t\t${3:// 构造函数}',
'\t}',
'',
'\t${4:// 方法}',
'}'
].join('\n')));
// 接口代码片段
this.snippets.set('interface', new TraeAPI.SnippetString([
'interface ${1:InterfaceName} {',
'\t${2:// 属性}',
'}'
].join('\n')));
// 异步函数代码片段
this.snippets.set('async', new TraeAPI.SnippetString([
'async function ${1:functionName}(${2:parameters}) {',
'\ttry {',
'\t\t${3:// 异步操作}',
'\t} catch (error) {',
'\t\tconsole.error(error);',
'\t}',
'}'
].join('\n')));
}
insertSnippet(key: string, position?: TraeAPI.Position) {
const snippet = this.snippets.get(key);
if (snippet && editor) {
editor.insertSnippet(snippet, position);
}
}
getAvailableSnippets(): string[] {
return Array.from(this.snippets.keys());
}
}事件处理
文档事件
typescript
// 文档变化事件
TraeAPI.workspace.onDidChangeTextDocument(event => {
console.log('文档已更改:', event.document.fileName);
console.log('变化数量:', event.contentChanges.length);
event.contentChanges.forEach(change => {
console.log('变化范围:', change.range);
console.log('新文本:', change.text);
});
});
// 文档保存事件
TraeAPI.workspace.onDidSaveTextDocument(document => {
console.log('文档已保存:', document.fileName);
// 保存后执行操作
if (document.languageId === 'typescript') {
// 运行 TypeScript 编译
TraeAPI.commands.executeCommand('typescript.build');
}
});
// 文档打开/关闭事件
TraeAPI.workspace.onDidOpenTextDocument(document => {
console.log('文档已打开:', document.fileName);
});
TraeAPI.workspace.onDidCloseTextDocument(document => {
console.log('文档已关闭:', document.fileName);
});编辑器事件
typescript
// 选择变化事件
TraeAPI.window.onDidChangeTextEditorSelection(event => {
console.log('选择已更改:', event.textEditor.document.fileName);
console.log('新选择:', event.selections);
// 根据选择更新状态栏
updateStatusBar(event.selections[0]);
});
// 可见范围变化事件
TraeAPI.window.onDidChangeTextEditorVisibleRanges(event => {
console.log('可见范围已更改:', event.visibleRanges);
});
// 编辑器选项变化事件
TraeAPI.window.onDidChangeTextEditorOptions(event => {
console.log('编辑器选项已更改:', event.options);
});
// 活动编辑器变化事件
TraeAPI.window.onDidChangeActiveTextEditor(editor => {
if (editor) {
console.log('活动编辑器:', editor.document.fileName);
// 更新语言特定的功能
updateLanguageFeatures(editor.document.languageId);
}
});高级功能
代码折叠
typescript
// 注册折叠范围提供程序
const foldingProvider: TraeAPI.FoldingRangeProvider = {
provideFoldingRanges(document, context, token) {
const ranges: TraeAPI.FoldingRange[] = [];
for (let i = 0; i < document.lineCount; i++) {
const line = document.lineAt(i);
// 检测函数开始
if (line.text.includes('function') || line.text.includes('{')) {
let endLine = i;
let braceCount = 0;
// 查找匹配的结束括号
for (let j = i; j < document.lineCount; j++) {
const currentLine = document.lineAt(j);
braceCount += (currentLine.text.match(/{/g) || []).length;
braceCount -= (currentLine.text.match(/}/g) || []).length;
if (braceCount === 0 && j > i) {
endLine = j;
break;
}
}
if (endLine > i) {
ranges.push(new TraeAPI.FoldingRange(i, endLine, TraeAPI.FoldingRangeKind.Region));
}
}
}
return ranges;
}
};
TraeAPI.languages.registerFoldingRangeProvider('typescript', foldingProvider);代码镜头
typescript
// 注册代码镜头提供程序
const codeLensProvider: TraeAPI.CodeLensProvider = {
provideCodeLenses(document, token) {
const codeLenses: TraeAPI.CodeLens[] = [];
for (let i = 0; i < document.lineCount; i++) {
const line = document.lineAt(i);
// 在函数上添加代码镜头
if (line.text.includes('function')) {
const range = new TraeAPI.Range(i, 0, i, line.text.length);
const codeLens = new TraeAPI.CodeLens(range);
codeLenses.push(codeLens);
}
}
return codeLenses;
},
resolveCodeLens(codeLens, token) {
// 解析代码镜头命令
codeLens.command = {
title: '运行函数',
command: 'extension.runFunction',
arguments: [codeLens.range]
};
return codeLens;
}
};
TraeAPI.languages.registerCodeLensProvider('typescript', codeLensProvider);内联提示
typescript
// 注册内联提示提供程序
const inlayHintsProvider: TraeAPI.InlayHintsProvider = {
provideInlayHints(document, range, token) {
const hints: TraeAPI.InlayHint[] = [];
for (let i = range.start.line; i <= range.end.line; i++) {
const line = document.lineAt(i);
// 为函数参数添加类型提示
const functionMatch = line.text.match(/function\s+(\w+)\s*\(([^)]*)\)/);
if (functionMatch) {
const params = functionMatch[2].split(',');
params.forEach((param, index) => {
const paramName = param.trim();
if (paramName) {
const position = new TraeAPI.Position(i, line.text.indexOf(paramName) + paramName.length);
const hint = new TraeAPI.InlayHint(position, ': string', TraeAPI.InlayHintKind.Type);
hints.push(hint);
}
});
}
}
return hints;
}
};
TraeAPI.languages.registerInlayHintsProvider('typescript', inlayHintsProvider);实用工具
文本处理工具
typescript
class TextUtils {
// 获取当前单词
static getCurrentWord(editor: TraeAPI.TextEditor): string | undefined {
const position = editor.selection.active;
const wordRange = editor.document.getWordRangeAtPosition(position);
return wordRange ? editor.document.getText(wordRange) : undefined;
}
// 获取当前行
static getCurrentLine(editor: TraeAPI.TextEditor): string {
const position = editor.selection.active;
return editor.document.lineAt(position.line).text;
}
// 获取选中文本或当前单词
static getSelectedTextOrWord(editor: TraeAPI.TextEditor): string {
if (!editor.selection.isEmpty) {
return editor.document.getText(editor.selection);
}
return this.getCurrentWord(editor) || '';
}
// 插入文本到指定位置
static async insertTextAt(editor: TraeAPI.TextEditor, position: TraeAPI.Position, text: string): Promise<boolean> {
return editor.edit(editBuilder => {
editBuilder.insert(position, text);
});
}
// 替换选中文本
static async replaceSelection(editor: TraeAPI.TextEditor, newText: string): Promise<boolean> {
return editor.edit(editBuilder => {
editBuilder.replace(editor.selection, newText);
});
}
// 在当前行后插入新行
static async insertLineAfter(editor: TraeAPI.TextEditor, text: string): Promise<boolean> {
const position = editor.selection.active;
const lineEnd = editor.document.lineAt(position.line).range.end;
return this.insertTextAt(editor, lineEnd, '\n' + text);
}
// 获取缩进字符串
static getIndentation(editor: TraeAPI.TextEditor, line: number): string {
const lineText = editor.document.lineAt(line).text;
const match = lineText.match(/^\s*/);
return match ? match[0] : '';
}
// 格式化代码块
static formatCodeBlock(code: string, language: string): string {
// 简单的代码格式化逻辑
const lines = code.split('\n');
let indentLevel = 0;
const indentSize = 2;
return lines.map(line => {
const trimmed = line.trim();
if (!trimmed) return '';
// 减少缩进
if (trimmed.startsWith('}') || trimmed.startsWith(']') || trimmed.startsWith(')'))
indentLevel = Math.max(0, indentLevel - 1);
const formatted = ' '.repeat(indentLevel * indentSize) + trimmed;
// 增加缩进
if (trimmed.endsWith('{') || trimmed.endsWith('[') || trimmed.endsWith('('))
indentLevel++;
return formatted;
}).join('\n');
}
}位置和范围工具
typescript
class PositionUtils {
// 创建位置
static createPosition(line: number, character: number): TraeAPI.Position {
return new TraeAPI.Position(line, character);
}
// 创建范围
static createRange(startLine: number, startChar: number, endLine: number, endChar: number): TraeAPI.Range {
return new TraeAPI.Range(startLine, startChar, endLine, endChar);
}
// 比较位置
static comparePositions(a: TraeAPI.Position, b: TraeAPI.Position): number {
if (a.line !== b.line) {
return a.line - b.line;
}
return a.character - b.character;
}
// 检查位置是否在范围内
static isPositionInRange(position: TraeAPI.Position, range: TraeAPI.Range): boolean {
return range.contains(position);
}
// 获取范围的中心位置
static getRangeCenter(range: TraeAPI.Range): TraeAPI.Position {
const startLine = range.start.line;
const endLine = range.end.line;
const centerLine = Math.floor((startLine + endLine) / 2);
if (startLine === endLine) {
const centerChar = Math.floor((range.start.character + range.end.character) / 2);
return new TraeAPI.Position(centerLine, centerChar);
}
return new TraeAPI.Position(centerLine, 0);
}
// 扩展范围
static expandRange(range: TraeAPI.Range, lines: number, characters: number): TraeAPI.Range {
const newStart = new TraeAPI.Position(
Math.max(0, range.start.line - lines),
Math.max(0, range.start.character - characters)
);
const newEnd = new TraeAPI.Position(
range.end.line + lines,
range.end.character + characters
);
return new TraeAPI.Range(newStart, newEnd);
}
}最佳实践
性能优化
批量操作
typescript// 好的做法:批量编辑 editor.edit(editBuilder => { changes.forEach(change => { editBuilder.replace(change.range, change.text); }); }); // 避免:多次单独编辑 // changes.forEach(change => { // editor.edit(editBuilder => { // editBuilder.replace(change.range, change.text); // }); // });事件处理优化
typescript// 使用防抖处理频繁事件 let timeout: NodeJS.Timeout; TraeAPI.workspace.onDidChangeTextDocument(event => { clearTimeout(timeout); timeout = setTimeout(() => { // 处理文档变化 processDocumentChange(event); }, 300); });装饰管理
typescript// 及时清理装饰 class DecorationManager { private decorations = new Map<string, TraeAPI.TextEditorDecorationType>(); setDecoration(key: string, options: TraeAPI.DecorationRenderOptions) { // 清理旧装饰 const old = this.decorations.get(key); if (old) { old.dispose(); } // 创建新装饰 const decoration = TraeAPI.window.createTextEditorDecorationType(options); this.decorations.set(key, decoration); return decoration; } dispose() { this.decorations.forEach(decoration => decoration.dispose()); this.decorations.clear(); } }
错误处理
typescript
// 安全的编辑器操作
async function safeEdit(editor: TraeAPI.TextEditor, editFunction: (editBuilder: TraeAPI.TextEditorEdit) => void): Promise<boolean> {
try {
return await editor.edit(editFunction);
} catch (error) {
console.error('编辑操作失败:', error);
TraeAPI.window.showErrorMessage(`编辑失败: ${error.message}`);
return false;
}
}
// 验证文档状态
function validateDocument(document: TraeAPI.TextDocument): boolean {
if (document.isClosed) {
TraeAPI.window.showWarningMessage('文档已关闭');
return false;
}
if (document.isDirty) {
TraeAPI.window.showInformationMessage('文档有未保存的更改');
}
return true;
}用户体验
typescript
// 提供进度反馈
async function processLargeFile(document: TraeAPI.TextDocument) {
await TraeAPI.window.withProgress({
location: TraeAPI.ProgressLocation.Notification,
title: '处理文件中...',
cancellable: true
}, async (progress, token) => {
const totalLines = document.lineCount;
for (let i = 0; i < totalLines; i++) {
if (token.isCancellationRequested) {
break;
}
// 处理行
await processLine(document.lineAt(i));
// 更新进度
progress.report({
increment: (100 / totalLines),
message: `处理第 ${i + 1}/${totalLines} 行`
});
}
});
}
// 智能选择
function smartSelect(editor: TraeAPI.TextEditor) {
const position = editor.selection.active;
const document = editor.document;
// 尝试选择单词
let wordRange = document.getWordRangeAtPosition(position);
if (wordRange) {
editor.selection = new TraeAPI.Selection(wordRange.start, wordRange.end);
return;
}
// 尝试选择行
const line = document.lineAt(position.line);
editor.selection = new TraeAPI.Selection(line.range.start, line.range.end);
}示例:完整的编辑器扩展
typescript
// 文本处理扩展示例
class TextProcessorExtension {
private decorationType: TraeAPI.TextEditorDecorationType;
private statusBarItem: TraeAPI.StatusBarItem;
constructor(private context: TraeAPI.ExtensionContext) {
this.decorationType = TraeAPI.window.createTextEditorDecorationType({
backgroundColor: 'rgba(255, 255, 0, 0.2)'
});
this.statusBarItem = TraeAPI.window.createStatusBarItem(
TraeAPI.StatusBarAlignment.Right,
100
);
this.initialize();
}
private initialize() {
// 注册命令
const commands = [
TraeAPI.commands.registerCommand('textProcessor.highlightDuplicates', () => {
this.highlightDuplicateWords();
}),
TraeAPI.commands.registerCommand('textProcessor.formatSelection', () => {
this.formatSelection();
}),
TraeAPI.commands.registerCommand('textProcessor.insertTemplate', () => {
this.insertTemplate();
})
];
// 注册事件监听器
const eventListeners = [
TraeAPI.window.onDidChangeActiveTextEditor(editor => {
this.updateStatusBar(editor);
}),
TraeAPI.window.onDidChangeTextEditorSelection(event => {
this.updateStatusBar(event.textEditor);
})
];
// 添加到订阅
this.context.subscriptions.push(
...commands,
...eventListeners,
this.decorationType,
this.statusBarItem
);
// 显示状态栏
this.statusBarItem.show();
}
private highlightDuplicateWords() {
const editor = TraeAPI.window.activeTextEditor;
if (!editor) return;
const document = editor.document;
const text = document.getText();
const words = text.match(/\b\w+\b/g) || [];
// 统计单词频率
const wordCount = new Map<string, number>();
words.forEach(word => {
const count = wordCount.get(word.toLowerCase()) || 0;
wordCount.set(word.toLowerCase(), count + 1);
});
// 查找重复单词的位置
const duplicateRanges: TraeAPI.Range[] = [];
const regex = /\b\w+\b/g;
let match;
while ((match = regex.exec(text)) !== null) {
const word = match[0].toLowerCase();
if (wordCount.get(word)! > 1) {
const startPos = document.positionAt(match.index);
const endPos = document.positionAt(match.index + match[0].length);
duplicateRanges.push(new TraeAPI.Range(startPos, endPos));
}
}
// 应用装饰
editor.setDecorations(this.decorationType, duplicateRanges);
TraeAPI.window.showInformationMessage(`高亮了 ${duplicateRanges.length} 个重复单词`);
}
private async formatSelection() {
const editor = TraeAPI.window.activeTextEditor;
if (!editor) return;
const selection = editor.selection;
const selectedText = editor.document.getText(selection);
if (selectedText.trim() === '') {
TraeAPI.window.showWarningMessage('请先选择要格式化的文本');
return;
}
// 简单的格式化:移除多余空格,规范化换行
const formatted = selectedText
.replace(/\s+/g, ' ') // 多个空格替换为单个空格
.replace(/\s*\n\s*/g, '\n') // 规范化换行
.trim();
const success = await editor.edit(editBuilder => {
editBuilder.replace(selection, formatted);
});
if (success) {
TraeAPI.window.showInformationMessage('文本格式化完成');
}
}
private async insertTemplate() {
const editor = TraeAPI.window.activeTextEditor;
if (!editor) return;
const templates = [
{ label: 'JavaScript 函数', value: 'function' },
{ label: 'TypeScript 接口', value: 'interface' },
{ label: 'React 组件', value: 'react' },
{ label: 'HTML 模板', value: 'html' }
];
const selected = await TraeAPI.window.showQuickPick(templates, {
placeHolder: '选择要插入的模板'
});
if (!selected) return;
let snippet: TraeAPI.SnippetString;
switch (selected.value) {
case 'function':
snippet = new TraeAPI.SnippetString([
'function ${1:functionName}(${2:parameters}) {',
'\t${3:// 函数体}',
'\treturn ${4:result};',
'}'
].join('\n'));
break;
case 'interface':
snippet = new TraeAPI.SnippetString([
'interface ${1:InterfaceName} {',
'\t${2:property}: ${3:type};',
'}'
].join('\n'));
break;
case 'react':
snippet = new TraeAPI.SnippetString([
'import React from \'react\';',
'',
'const ${1:ComponentName}: React.FC = () => {',
'\treturn (',
'\t\t<div>',
'\t\t\t${2:// JSX content}',
'\t\t</div>',
'\t);',
'};',
'',
'export default ${1:ComponentName};'
].join('\n'));
break;
case 'html':
snippet = new TraeAPI.SnippetString([
'<!DOCTYPE html>',
'<html lang="zh-CN">',
'<head>',
'\t<meta charset="UTF-8">',
'\t<meta name="viewport" content="width=device-width, initial-scale=1.0">',
'\t<title>${1:页面标题}</title>',
'</head>',
'<body>',
'\t${2:// 页面内容}',
'</body>',
'</html>'
].join('\n'));
break;
default:
return;
}
await editor.insertSnippet(snippet);
}
private updateStatusBar(editor?: TraeAPI.TextEditor) {
if (!editor) {
this.statusBarItem.hide();
return;
}
const document = editor.document;
const selection = editor.selection;
let text = `行 ${selection.active.line + 1}, 列 ${selection.active.character + 1}`;
if (!selection.isEmpty) {
const selectedText = document.getText(selection);
const lines = selectedText.split('\n').length;
const chars = selectedText.length;
text += ` (已选择 ${lines} 行, ${chars} 字符)`;
}
this.statusBarItem.text = text;
this.statusBarItem.show();
}
}
// 激活扩展
export function activate(context: TraeAPI.ExtensionContext) {
new TextProcessorExtension(context);
}编辑器 API 为 Trae IDE 扩展开发提供了强大而灵活的文本编辑功能。通过合理使用这些 API,您可以创建出功能丰富、用户体验良好的编辑器扩展。