李のメニュー

最近、私はまだ座ってVimやEmacsに似ているがHaskellで書かれたテキストエディターであるYiを扱うことにしました。 キットにはVimおよびEmacsシミュレーションも含まれています。

VimまたはEmacsの経験が不足しているため、Cuaシミュレーションのみが適しています。 ホットキーはほとんどありませんが、私にはよく知られています。 それで、私はそれから始めて、自分のために設定を書くことに決めました。

通常のグラフィックエディターでは、メニューを使用する便利な方法のように思えます。 Altキーを押すと、メニューが開き、各要素に下線付きの文字が表示されます。この要素をクリックして選択します。

したがって、すべてのコマンドを一度に記憶する必要はありませんが、メニューを覗いて徐々に使い始めることができます。

私はYiで似たようなものを台無しにすることにしました。



画像





簡単なホットキーを設定します



最初に、Yiの仕組みを理解する必要があります。 これを理解する最も簡単な方法は、 Cuaなどの既製のバインダーを見ることです。 これは切り捨てられており、VimおよびEmacsのアナログバインダーよりもはるかに原始的ですが、私たちの目的(独自の記述)のために-それだけです。



まず、ホットキーの一般的な定義方法に注目しましょう。 これはメイン関数で見ることができます。

keymap :: KeymapSet keymap = portableKeymap ctrl -- | Introduce a keymap that is compatible with both windows and osx, -- by parameterising the event modifier required for commands portableKeymap :: (Event -> Event) -> KeymapSet portableKeymap cmd = modelessKeymapSet $ selfInsertKeymap <|> move <|> select <|> rect <|> other cmd
      
      





さまざまなバインドオプションは、<|>演算子を使用して結合されます。 他のコマンドをさらに見てみましょう:

 other cmd = choice [ spec KBS ?>>! deleteSel bdeleteB, spec KDel ?>>! deleteSel (deleteN 1), spec KEnter ?>>! replaceSel "\n", cmd (char 'q') ?>>! askQuitEditor, cmd (char 'f') ?>> isearchKeymap Forward, cmd (char 'x') ?>>! cut, cmd (char 'c') ?>>! copy, cmd (char 'v') ?>>! paste, cmd (spec KIns) ?>>! copy, shift (spec KIns) ?>>! paste, cmd (char 'z') ?>>! undoB, cmd (char 'y') ?>>! redoB, cmd (char 's') ?>>! fwriteE, cmd (char 'o') ?>>! findFile, cmd (char '/') ?>>! withModeB modeToggleCommentSelection, cmd (char ']') ?>>! autoIndentB IncreaseOnly, cmd (char '[') ?>>! autoIndentB DecreaseOnly ]
      
      





ご覧のとおり、左側にキーボードショートカット、右側にアクションがあります。 つまり cmd(char 'c')を押すと(デフォルトではcmdはctrlです)-コピーが取得され、そのコードも簡単です。



これらの定義を自分にコピーし、何らかのメニューを作成するために編集を開始することにしました。

メニューの作り方



メニューの実装方法を決定するには、モジュールのドキュメントに移動する必要があります 。 すべてが非常に便利に構成されており、 Yi.MiniBufferモジュールが目を引きます。 どうやら、これが必要なものです。 機能があります

 spawnMinibufferE :: String -> KeymapEndo -> EditorM BufferRef
      
      





表示されたテキストと、キーへのバインディングを公開する関数を受け入れます。 つまり 必要なもの。 行にメニュー項目を表示し、バインダーではキーによるメニュー項目の選択をキャッチします。



まず、メニューの説明に便利なタイプを作成します。 メニューは項目のリストで構成され、各項目はサブメニューを開くか、何らかのアクションです。 だから私たちは書く:

 -- | Menu type Menu = [MenuItem] -- | Menu utem data MenuItem = MenuAction String (MenuContext -> Char -> Keymap) | SubMenu String Menu -- | Menu action context data MenuContext = MenuContext { parentBuffer :: BufferRef }
      
      





SubMenuバリアントにはタイトルとサブメニューが含まれ、MenuActionバリアントにはタイトルと必要なバインダーを作成する関数が含まれます。

MenuContextはアクションに転送される特定のコンテキストです(これまでのところ、メニューが呼び出されたソースバッファーのみがあり、保存ボタンを実装するために必要でした)。Charは、メニューを呼び出す必要のあるクリックによるボタンです。



型は再帰的であるため、単純に畳み込みを定義して、後でそれを使用してメニューを開始できます。

 -- | Fold menu item foldItem :: (String -> (MenuContext -> Char -> Keymap) -> a) -> (String -> [a] -> a) -> MenuItem -> a foldItem mA sM (MenuAction title act) = mA title act foldItem mA sM (SubMenu title sm) = sM title (map (foldItem mA sM) sm) -- | Fold menu foldMenu :: (String -> (MenuContext -> Char -> Keymap) -> a) -> (String -> [a] -> a) -> Menu -> [a] foldMenu mA sM = map (foldItem mA sM)
      
      







また、メニュー項目をより便利に作成する関数も必要です。 SubMenuは簡単に作成でき、SubMenuは「ファイル」です...しかし、MenuActionは使いにくいです。 したがって、アクションを実行するいくつかの関数を定義します(右側の場合と同じですか?>>!バインダー内)。 それらのうちの2つのコードを提供します。

 -- | Action on item action_ :: (YiAction ax, Show x) => String -> a -> MenuItem action_ title act = action title (const act) -- | Action on item with context action :: (YiAction ax, Show x) => String -> (MenuContext -> a) -> MenuItem action title act = MenuAction title act' where act' ctx c = char c ?>>! (do withEditor closeBufferAndWindowE runAction $ makeAction (act ctx))
      
      





ここで、適切なボタン(char c)をクリックするとメニューを閉じ、必要なアクションを呼び出すMenuItemを作成します。



最後に、メニュー表示機能を作成します。

 -- | Start menu action startMenu :: Menu -> EditorM () startMenu m = do --  ,   ctx <- fmap MenuContext (gets currentBuffer) startMenu' ctx m where --  ,      (, ) startMenu' ctx = showMenu . foldMenu onItem onSub where showMenu :: [(String, Maybe Keymap)] -> EditorM () --   —      ,    showMenu is = void $ spawnMinibufferE menuItems (const (subMap is)) where menuItems = (intercalate " " (map fst is)) --    — --   +    ,   onItem title act = (title, fmap (act ctx) (menuEvent title)) where --    — --  +  ,        onSub title is = (title, fmap subMenu (menuEvent title)) where --  'c'        subMenu c = char c ?>>! closeBufferAndWindowE >> showMenu is --        Esc,        subMap is = choice $ closeMenu : mapMaybe snd is where closeMenu = spec KEsc ?>>! closeBufferAndWindowE
      
      





完全なコードはこちらにあります



メニューを作成する



これで、何らかのメニューを作成して、ボタンにリンクする価値があり、確認を開始できます。

最初に、私はYiのさまざまなモジュールを見ながら出会ったものを詰め込んだ、大きくて緑豊かなメニューを書きました。 たとえば、[表示-Windows]サブメニューに頻繁に移動することに気付いたとき、このメニューを別のホットキーに配置することにしました。

これで、長いVWSの組み合わせだけでなく、Ctrl-W-Sだけでウィンドウを分割できます。



Windowsのメインメニューとサブメニューのコードは次のとおりです。

 -- | Main menu mainMenu :: Menu mainMenu = [ menu "File" [ action_ "Quit" askQuitEditor, action "Save" (fwriteBufferE . parentBuffer)], menu "Edit" [ action_ "Auto complete" wordComplete, action_ "Completion" completeWordB], menu "Tools" [ menu "Ghci" ghciMenu], menu "View" [ menu "Windows" windowsMenu, menu "Tabs" tabsMenu, menu "Buffers" buffersMenu, menu "Layout" [ action_ "Next" layoutManagersNextE, action_ "Previous" layoutManagersPreviousE]]] -- | Windows menu windowsMenu :: Menu windowsMenu = [ action_ "Next" nextWinE, action_ "Previous" prevWinE, action_ "Split" splitE, action_ "sWap" swapWinWithFirstE, action_ "Close" tryCloseE, action_ "cLose-all-but-this" closeOtherE]
      
      





ここでメニュー全体を見ることができます



結果



さまざまな組み合わせのメインメニューとサブメニューを規定しています。



私たちはそれを使用しています!





まとめ



実装後、ある種の組み込みの単純なオートコンプリート、次にGHCiインタープリターをねじ込みました。 次のステップは、hlintツール(コードを分析し、標準関数の使用によってコードを置き換えることができる場所、余分なものが記述されている場所など)です。



すべてのコードはGitHubで入手できます



更新する


3つの関数actionB



actionE



およびactionY



。 ただし、 runAction . makeAction



に置き換えることができますrunAction . makeAction



runAction . makeAction





単一のアクションのために修正



All Articles