# myWikiエディターマクロ

## 概要

myWikiで利用できるエディターマクロについての説明です。

スクリプトエンジンは [DWScript](https://github.com/EricGrange/DWScript) を利用しており、Delphiをベースにした文法になっています。

myWiki独自の定数およびオブジェクトが利用可能です。

## ショートカットキー

以下の場所で設定します。同じMacroIDに対して複数の設定がある場合は、数字の小さいものが優先されます。

1. `macro`フォルダーの`Shortcut.txt`設定  
   (サンプルとして`Shortcut.txt.example`が置いてありますので参考にしてください)
2. マクロファイルの設定

複数のマクロに対して同じショートカットを割り当てたり、ほかの機能のショートカットと重複した場合はうまく動作しません。

## マクロファイル

`macro`フォルダーに拡張子`.md`でファイルを作成します。

### サンプル

~~~
# MacroID

## Name

Macro Name

## Category

Category Name

## Notation

- Markdown
- PukiWiki
- Text

## Shortcut

Ctrl+Shift+O

## Code

```pascal
ShowMessage('Hello, World!');
```
~~~

### サンプルの説明

- 先頭に`#`とマクロIDを書いてください。マクロIDはほかのマクロと重複しないものを英数字で記入してください
- `## Name`にマクロの名称を記入してください。メニューに表示される名前になります
- `## Category`にカテゴリーの名称を記入してください。メニューに表示される名前になります
- `## Notation`に対応する書式を記入してください。次の4種類が有効です。`All`を指定するとすべてで有効になります
  - `Markdown`
  - `PukiWiki`
  - `Text`
  - `All`
- `## Shortcut`にショートカットキーを記入してください。ショートカットキーを割り当てない場合は記載不要です
  - Delphiの[キーおよびショートカットの表現](https://docwiki.embarcadero.com/RADStudio/Sydney/ja/%E3%82%AD%E3%83%BC%E3%81%8A%E3%82%88%E3%81%B3%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%AB%E3%83%83%E3%83%88%E3%81%AE%E8%A1%A8%E7%8F%BE)の文字列表現で書きます
- `## Code`にマクロのコードを書きます。前後を` ``` `で囲みます。1つめの` ``` `の後ろには文字を記載できます

## 定数

マクロで利用できる定数です。

### 書式

| 定数   | 値     | 説明 |
| ------ | ------ | --- |
| `NotationMarkdown` | 1 | Markdown |
| `NotationPukiWiki` | 2 | PukiWiki |
| `NotationText`     | 3 | テキスト |

### エディターコマンド

[SynEdit](https://github.com/pyscripter/SynEdit) のエディターコマンドが利用できます。`Editor.ExecuteCommand` メソッドの引数として使用します。

| 定数   | 説明 |
| ------ | ------ |
| `ecLeft`           | カーソルを1つ左に移動します |
| `ecRight`          | カーソルを1つ右に移動します |
| `ecUp`             | カーソルを1つ上に移動します |
| `ecDown`           | カーソルを1つ下に移動します |
| `ecWordLeft`       | カーソルを1単語左に移動します |
| `ecWordRight`      | カーソルを1単語右に移動します |
| `ecLineStart`      | カーソルを行頭に移動します(行頭に空白がある場合行頭と単語の先頭に交互に移動します) |
| `ecLineEnd`        | カーソルを行末に移動します(行末に空白がある場合行末と単語の末尾に交互に移動します) |
| `ecPageUp`         | 1ページ分上に移動します |
| `ecPageDown`       | 1ページ分下に移動します |
| `ecEditorTop`      | テキストの先頭に移動します |
| `ecEditorBottom`   | テキストの末尾に移動します |
| `ecSelectAll`      | すべて選択します |
| `ecCopy`           | 選択範囲をコピーします |
| `ecCut`            | 選択範囲を切り取ります |
| `ecPaste`          | 貼り付けます |
| `ecUndo`           | 元に戻します |
| `ecRedo`           | やり直します |
| `ecDeleteChar`     | カーソル位置の文字を削除します |
| `ecDeleteLastChar` | カーソル前の文字を削除します |
| `ecDeleteLine`     | 現在の行を削除します |
| `ecLineBreak`      | 改行を挿入します |
| `ecBlockIndent`    | 選択範囲をインデントします |
| `ecBlockUnindent`  | 選択範囲をアンインデントします |
| `ecUpperCase`      | 選択範囲を大文字にします |
| `ecLowerCase`      | 選択範囲を小文字にします |

その他多数のコマンドが利用可能です。詳細はGitHubの `SynEditKeyCmds.pas` を参照してください。

## Editorオブジェクト

エディターを操作するためのオブジェクトです。

### プロパティ

| プロパティ | 型 | 説明 |
| --- | --- | --- |
| `Text`       | `String`  | エディター全体のテキストを取得または設定します |
| `SelText`    | `String`  | 選択範囲のテキストを取得または設定します |
| `LineCount`  | `Integer` | テキストの総行数を取得します |
| `CaretX`     | `Integer` | カーソルの桁位置を取得または設定します |
| `CaretY`     | `Integer` | カーソルの行位置を取得または設定します |
| `Notation`   | `Integer` | 現在の書式を取得します(`NotationMarkdown`など) |
| `BlockBegin` | `TPoint`  | 選択範囲の開始位置(`X`:文字位置, `Y`:行)を取得します |
| `BlockEnd`   | `TPoint`  | 選択範囲の終了位置(`X`:文字位置, `Y`:行)を取得します |

### メソッド

#### AppendTextAtLine
```pascal
procedure AppendTextAtLine(const Line: Integer; const Text: String);
```
指定した行の末尾にテキストを追加します。`Line`が存在しない場合は、その行まで空行が追加されます。

- `Line`: 追加先の行番号 (1ベース)
- `Text`: 追加するテキスト

#### BeginUndoBlock
```pascal
procedure BeginUndoBlock;
```
一連の操作をまとめて元に戻せるように、アンドゥブロックを開始します。`EndUndoBlock`と対で使います。

#### DeleteLine
```pascal
procedure DeleteLine(const Line: Integer);
```
指定した行を削除します。

- `Line`: 削除する行番号 (1ベース)

#### EndUndoBlock
```pascal
procedure EndUndoBlock;
```
アンドゥブロックを終了します。

#### ExecuteCommand
```pascal
procedure ExecuteCommand(const Command: Integer);
```
エディターコマンドを実行します。定数を参照してください。

- `Command`: 実行するエディターコマンド (`ecLeft`など)

#### GetLine
```pascal
function GetLine(const Line: Integer): String;
```
指定した行のテキストを取得します。

- `Line`: 取得する行番号 (1ベース)
- **戻り値**: その行のテキスト

#### Insert
```pascal
procedure Insert(const Text: String);
```
現在のカーソル位置にテキストを挿入します。範囲選択中の場合は、選択範囲を解除してからテキストを挿入します。

- `Text`: 挿入するテキスト

#### InsertLine
```pascal
procedure InsertLine(const Line: Integer; const Text: String);
```
指定した行に新しい行としてテキストを挿入します。元の行は下に移動します。

- `Line`: 挿入先の行番号 (1ベース)
- `Text`: 挿入するテキスト

#### InsertTextAtLineStart
```pascal
procedure InsertTextAtLineStart(const Line: Integer; const Text: String);
```
指定した行の先頭にテキストを挿入します。

- `Line`: 挿入先の行番号 (1ベース)
- `Text`: 挿入するテキスト

#### MoveCaret
```pascal
procedure MoveCaret(const Line, Char: Integer);
```
カーソルを指定した位置に移動します。

- `Line`: 移動先の行番号 (1ベース)
- `Char`: 移動先の文字位置 (1ベース)

#### SelectRange
```pascal
procedure SelectRange(const StartLine, StartChar, EndLine, EndChar: Integer);
```
指定した範囲を選択します。

- `StartLine`: 選択開始行 (1ベース)
- `StartChar`: 選択開始文字位置 (1ベース)
- `EndLine`: 選択終了行 (1ベース)
- `EndChar`: 選択終了文字位置 (1ベース)

#### SetLine
```pascal
procedure SetLine(const Line: Integer; const Text: String);
```
指定した行のテキストを置き換えます。

- `Line`: 置き換える行番号 (1ベース)
- `Text`: 新しい行のテキスト

## RegExオブジェクト

正規表現を扱うためのオブジェクトです。

### メソッド

#### IsMatch
```pascal
function IsMatch(const Input, Pattern: String; const IgnoreCase, MultiLine: Boolean): Boolean;
```
入力文字列が正規表現パターンに一致するかどうかを調べます。

- `Input`: 入力文字列
- `Pattern`: 正規表現パターン
- `IgnoreCase`: 大文字/小文字を無視するか
- `MultiLine`: 複数行モードか
- **戻り値**: 一致した場合は `True`

#### Match
```pascal
function Match(const Input, Pattern: String; const IgnoreCase, MultiLine: Boolean): String;
```
入力文字列内で、正規表現に最初に一致した部分を検索します。一致した結果は、`Success`, `Value`, `Index`, `Length`, `GroupCount`, `GroupValue` で取得できます。

- `Input`: 入力文字列
- `Pattern`: 正規表現パターン
- `IgnoreCase`: 大文字/小文字を無視するか
- `MultiLine`: 複数行モードか
- **戻り値**: 一致した文字列

#### Replace
```pascal
function Replace(const Input, Pattern, Replacement: String; const IgnoreCase, MultiLine: Boolean): String;
```
正規表現に一致したすべての部分を指定した文字列で置換します。

- `Input`: 入力文字列
- `Pattern`: 正規表現パターン
- `Replacement`: 置換文字列
- `IgnoreCase`: 大文字/小文字を無視するか
- `MultiLine`: 複数行モードか
- **戻り値**: 置換後の文字列

#### Success
```pascal
function Success: Boolean;
```
直前の `Match` が成功したかどうかを返します。

- **戻り値**: 成功した場合は `True`

#### Value
```pascal
function Value: String;
```
直前の `Match` で一致した文字列を返します。

- **戻り値**: 一致した文字列

#### Index
```pascal
function Index: Integer;
```
直前の `Match` で一致した箇所の開始インデックスを返します (1ベース)。

- **戻り値**: 開始インデックス。一致しなかった場合は0

#### Length
```pascal
function Length: Integer;
```
直前の `Match` で一致した文字列の長さを返します。

- **戻り値**: 文字列の長さ

#### GroupCount
```pascal
function GroupCount: Integer;
```
直前の `Match` でキャプチャされたグループの数を返します。

- **戻り値**: グループ数

#### GroupValue
```pascal
function GroupValue(const Index: Integer): String;
```
インデックスで指定されたキャプチャグループの値を取得します。グループ0は一致した文字列全体です。

- `Index`: グループのインデックス (0ベース)
- **戻り値**: キャプチャされた文字列

## 組み込み(GUI)

### メソッド

#### InputBox
```pascal
function InputBox(const ACaption, APrompt, ADefault: String): String;
```
値をユーザーが入力できる入力ダイアログを表示します。

- `ACaption`: ダイアログのキャプション
- `APrompt`: 編集ボックスのラベル
- `ADefault`: 編集ボックスに初期表示される値
- **戻り値**: OKボタンを押すと入力した値。キャンセルボタンを押すと初期表示値

#### ShowMessage
```pascal
procedure ShowMessage(const Msg: String);
```
メッセージダイアログを表示します。

- `Msg`: 表示するメッセージ
