goo blog サービス終了のお知らせ 

CyberChaos(さいばかおす)

プログラミング言語、トランスパイラ、RPA、ChatGPT、データマイニング、リバースエンジニアリングのための忘備録

Android用Obsidianのソースコード文法チェック・修正アドイン

2025-05-17 20:12:12 | アイデア起業・アイデア売買

Deep Seekの回答だから多分、何回もソースコードを修正させなくちゃいかんだろうけどwww

以下はAndroidのObsidianでも動作する、軽量なコード文法チェックアドインの実装です。このバージョンではシンプルな正規表現ベースのチェックを使用します。

## Android対応版コードチェックアドイン

### `main.ts` (メインアドインファイル)

```typescript
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';

interface CodeLinterSettings {
autoFix: boolean;
checkForSemicolons: boolean;
checkConsoleLog: boolean;
}

const DEFAULT_SETTINGS: CodeLinterSettings = {
autoFix: true,
checkForSemicolons: true,
checkConsoleLog: true
}

export default class CodeLinterPlugin extends Plugin {
settings: CodeLinterSettings;

async onload() {
await this.loadSettings();

this.addCommand({
id: 'lint-current-code',
name: 'Lint current code block',
editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView) => {
if (checking) {
return !!this.getCodeBlockRange(editor);
}
this.lintCodeBlock(editor);
}
});

this.addSettingTab(new CodeLinterSettingTab(this.app, this));
}

async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}

async saveSettings() {
await this.saveData(this.settings);
}

getCodeBlockRange(editor: Editor): { code: string, from: number, to: number } | null {
const cursor = editor.getCursor();
const content = editor.getValue();
const lines = content.split('\n');
let startLine = -1;
let endLine = -1;
for (let i = cursor.line; i >= 0; i--) {
if (lines[i].startsWith('```')) {
startLine = i;
break;
}
}
for (let i = cursor.line; i
if (lines[i].startsWith('```')) {
endLine = i;
break;
}
}
if (startLine === -1 || endLine === -1 || startLine === endLine) {
return null;
}
const code = lines.slice(startLine + 1, endLine).join('\n');
const from = editor.offsetToPos(content.indexOf(lines[startLine]));
const to = editor.offsetToPos(content.indexOf(lines[endLine]) + { line: 0, ch: lines[endLine].length });
return { code, from, to };
}

async lintCodeBlock(editor: Editor) {
const codeBlock = this.getCodeBlockRange(editor);
if (!codeBlock) {
new Notice('コードブロックが見つかりません');
return;
}

const { code, from, to } = codeBlock;
const language = editor.getLine(from.line).substring(3).trim();
if (!language) {
new Notice('コードブロックに言語指定がありません');
return;
}

new Notice(`${language}コードをチェック中...`);
// 簡易的なチェックを実行
const result = this.simpleLint(code, language);
if (result.fixedCode !== null && this.settings.autoFix) {
// コードを修正
const newContent = '```' + language + '\n' + result.fixedCode + '\n```';
editor.replaceRange(newContent, from, to);
new Notice(`コードを修正しました (${result.issues.length}件の問題)`);
} else if (result.issues.length > 0) {
// 問題を表示
new Notice(`${result.issues.length}件の問題が見つかりました`);
new LintResultsModal(this.app, result.issues).open();
} else {
new Notice('問題は見つかりませんでした');
}
}

simpleLint(code: string, language: string): { issues: LintIssue[], fixedCode: string | null } {
const issues: LintIssue[] = [];
let fixedCode = code;
// JavaScript/TypeScript向けの簡易チェック
if (['javascript', 'js', 'typescript', 'ts'].includes(language.toLowerCase())) {
if (this.settings.checkForSemicolons) {
// セミコロンなしの行をチェック
const lines = code.split('\n');
lines.forEach((line, index) => {
if (line.trim() &&
!line.trim().endsWith(';') &&
!line.trim().endsWith('{') &&
!line.trim().endsWith('}') &&
!line.includes('//') &&
!line.trim().startsWith('if') &&
!line.trim().startsWith('for') &&
!line.trim().startsWith('while') &&
!line.trim().startsWith('function')) {
issues.push({
line: index + 1,
message: 'Missing semicolon',
severity: 'warning'
});
// 自動修正
if (this.settings.autoFix) {
fixedCode = fixedCode.replace(line, line + ';');
}
}
});
}
if (this.settings.checkConsoleLog) {
// console.logをチェック
const consoleLogRegex = /console\.log\(.*\)/g;
let match;
while ((match = consoleLogRegex.exec(code)) !== null) {
const line = code.substring(0, match.index).split('\n').length;
issues.push({
line,
message: 'console.log found',
severity: 'warning'
});
// 自動修正 (コメントアウト)
if (this.settings.autoFix) {
fixedCode = fixedCode.replace(match[0], '// ' + match[0]);
}
}
}
}
return {
issues,
fixedCode: this.settings.autoFix ? fixedCode : null
};
}
}

interface LintIssue {
line: number;
message: string;
severity: 'error' | 'warning';
}

class LintResultsModal extends Modal {
constructor(app: App, private issues: LintIssue[]) {
super(app);
}

onOpen() {
const { contentEl } = this;
contentEl.createEl('h2', { text: 'コードチェック結果' });

if (this.issues.length === 0) {
contentEl.createEl('p', { text: '問題は見つかりませんでした' });
return;
}

const list = contentEl.createEl('ul');
this.issues.forEach(issue => {
const item = list.createEl('li');
item.createEl('strong', {
text: `${issue.line}行目: ${issue.message}`,
cls: issue.severity === 'error' ? 'error' : 'warning'
});
});
}

onClose() {
const { contentEl } = this;
contentEl.empty();
}
}

class CodeLinterSettingTab extends PluginSettingTab {
plugin: CodeLinterPlugin;

constructor(app: App, plugin: CodeLinterPlugin) {
super(app, plugin);
this.plugin = plugin;
}

display(): void {
const { containerEl } = this;

containerEl.empty();

containerEl.createEl('h2', { text: 'コードチェック設定' });

new Setting(containerEl)
.setName('自動修正')
.setDesc('見つかった問題を自動的に修正する')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.autoFix)
.onChange(async (value) => {
this.plugin.settings.autoFix = value;
await this.plugin.saveSettings();
}));

new Setting(containerEl)
.setName('セミコロンをチェック')
.setDesc('JavaScript/TypeScriptでセミコロンがない行を警告する')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.checkForSemicolons)
.onChange(async (value) => {
this.plugin.settings.checkForSemicolons = value;
await this.plugin.saveSettings();
}));

new Setting(containerEl)
.setName('console.logをチェック')
.setDesc('console.logの使用を警告する')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.checkConsoleLog)
.onChange(async (value) => {
this.plugin.settings.checkConsoleLog = value;
await this.plugin.saveSettings();
}));
}
}
```

### `manifest.json`

```json
{
"id": "simple-code-linter",
"name": "Simple Code Linter",
"version": "0.1.0",
"minAppVersion": "0.15.0",
"description": "軽量なコードチェッカー for Android Obsidian",
"author": "Your Name",
"authorUrl": "",
"isDesktopOnly": false
}
```

## Android用アドインの特徴

1. **外部依存なし**: Node.jsやESLintに依存しない純粋なTypeScript実装
2. **軽量**: 正規表現ベースの簡易チェックのみ
3. **Android対応**: `isDesktopOnly: false` でモバイル対応
4. **基本機能**:
- セミコロン抜けの検出
- console.logの使用検出
- 簡単な自動修正機能

## インストール方法 (Android)

1. 上記のコードを`main.ts`と`manifest.json`として保存
2. プロジェクトをビルド (`npx tsc`)
3. 生成された`main.js`と`manifest.json`をZIPに圧縮
4. AndroidのObsidianで:
- 設定 → コミュニティプラグイン → インストール済みプラグイン
- 「...」メニューから「Load from ZIP」を選択
- ZIPファイルを選択してインストール

## 追加実装のヒント

より多くの言語をサポートするには、`simpleLint`メソッドを拡張してください。例えば:

```typescript
// Python向けのチェックを追加
if (language.toLowerCase() === 'python') {
// インデント不整合のチェック
// == の代わりに is を使っているチェックなど
}

// 他の言語チェックも同様に追加
```

このアドインは基本的なチェックのみを行いますが、Android環境でも確実に動作し、Obsidianのモバイル体験を損なうことなくコード品質を向上させるのに役立ちます。


最新の画像もっと見る

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。