Quantcast
Channel: Vimタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 5608

AtomでVimのモードラインを限定的に解釈する

$
0
0

はじめに

Vimにはモードラインという、ファイル自体に記述したVimの各種オプションを読み取り、ファイルを開く際に自動で設定を行う機能がある。
例えば下記のようなコメントをRubyプログラムの先頭または末尾あたりに書いておくとする。

# vim:set expandtab tabstop=2 fenc=euc-jp ff=unix ft=ruby:

すると、このファイルを開く際、Vimは自動で

  • Tabの代わりに空白2文字を挿入
  • 文字コードはeuc-jp
  • 改行コードはLF
  • ファイルタイプはRuby

とエディタの設定を変更してくれる。

詳しくはマニュアルの日本語訳を読んでいただきたいが、VimからAtomに移行した際にはこのモードラインを解釈してほしいケースがときたま存在する。
物は試しとモードラインの解釈から設定の反映までを試みることにした。

もちろん、VimとAtomでは設定項目が異なってくるため、ひとまずは個人的によく使う「文字コード」「改行コード」「ファイルタイプ」の設定を行うことを目標とする。

モードラインのパース

モードラインの書式は上記のマニュアル内に記載があるため、それらに沿った書式を正規表現でパースすることとした。

parseVimModeLine = (line) ->regexp = /(vi|vim|\s+ex):\s*(se(t)*)*\s+([^:]+)*\s*:/matches = line.matchregexpoptions = nullifmatchesoptions = {}foroptioninmatches[4].split(" ")[key,value]=option.split"="options[key]=value?trueifkey!=""options

「はじめに」に記載した例を上記関数に渡すと下記のようなオブジェクトを返してくれる。

{expandtab: true,tabstop: "2",fenc: "euc-jp",ff: "unix",ft: "ruby"}

文字コードのセット

encoding-selectordetectEncodingを参考に、文字コードのセットを行う部分だけを取り出した。
iconv-liteが対応している文字コードしか通せないため、例えばeucJP-msと記載されていた場合はfalseを返すこととなる。

setEncoding = (encoding) ->iconv = require'iconv-lite'editor = atom.workspace.getActiveTextEditor()returnfalseunlessiconv.encodingExists(encoding)encoding = encoding.toLowerCase().replace(/[^0-9a-z]|:\d{4}$/g,'')editor.setEncoding(encoding)

上記parseVimModeLineの返り値のfileencodingまたはfencの値を渡せば設定可能となる。

options = parseVimModeLine(line)setEncoding(options.fileencoding?options.fenc)

上記のような書き方の場合、fileencodingfencどちらも設定がない場合はundefinedが渡ることとなるが、特にエラーが発生する訳でもないのでプロパティの存在チェックは行っていない。

改行コードのセット

line-ending-selectorの実装を参考に改行コードの設定を行う関数を実装した。
設定と同時にバッファ内の改行コードを置換するようにしているため、実際の改行コードとモードラインに記載された改行コードが異なる場合は多少注意が必要となる。

setLineEnding = (lineEnding) ->editor = atom.workspace.getActiveTextEditor()buffer = editor?.getBuffer()returnunlessbufferbuffer.setPreferredLineEnding(lineEnding)buffer.setText(buffer.getText().replace(/\r\n|\r|\n/g,buffer.getPreferredLineEnding()))

モードラインに記載するものはunixdosmacという記法であるため、これらと実際の改行コードを結び付けてやる必要がある。

format   = options.fileformat?options.fflineEnding: {unix: "\n"dos: "\r\n"mac: "\r"}setLineEnding(lineEnding[format])

なお、CRはline-ending-selectorの選択肢には表示されていないが、試してみたところ正しく設定がされ、ステータスバーの改行コード欄もCRと表示されていた。

ファイルタイプのセット

2015/11/09 atom.grammars.selectGrammarを利用するように修正

grammar-selectorの実装を参考にファイルタイプの設定を行う関数を実装したのだが、atom.grammars.selectGrammarといううってつけのメソッドが存在するため書き換えた。
この場合、例えばCore Packagesのlanguage-ruby-on-railsのものであるRuby on Railsは選択されず、railsプロジェクトであってもRubyが選択されてしまうこととなる。
今のところAtomでrailsプロジェクトを扱う予定はないのでこのまま利用しようと思うが、railsプロジェクトと通常のRubyプログラムを書き分ける際は何かしら解決策を考える必要がある。

setFileType = (type) ->editor = atom.workspace.getActiveTextEditor()returnunlesseditorgrammar = atom.grammars.selectGrammar(type)ifgrammarisntatom.grammars.nullGrammaratom.grammars.setGrammarOverrideForPath(editor.getPath(),grammar)editor.setGrammar(grammar)

利用する際はパースしたfiletypeftの値をそのまま引数に渡すようにしたが、場合によっては読み替えを行うようにする必要がある。

setFileType(options.filetype?options.ft)

現時点ではこれで困らなかったのでそのまま利用しているが、必要ならば「改行コードのセット」のように読み替え用のオブジェクトを用意し、undefinedであればfiletypeに設定されているものをそのまま渡すようにするとよいと思う。

ファイルの読み込み時に実行する

2015/11/09 atom.grammars.selectGrammarを利用するように修正

上記の関数をinit.coffeeに記載し、ファイルの読み込み時に実行されるようにする。

atom.workspace.onDidOpen->unlessdetectVimModeLine()detectEncoding()detectVimModeLine = ->editor = atom.workspace.getActiveTextEditor()lineEnding: {unix: "\n"dos: "\r\n"mac: "\r"}tryfirstLine = editor.lineTextForBufferRow(0)lastLine  = editor.lineTextForBufferRow(editor.getLastBufferRow())catcherrorreturnfalseoptions = parseVimModeLine(firstLine)?parseVimModeLine(lastLine)returnfalseunlessoptionsencoding = options.fileencoding?options.fencformat   = options.fileformat?options.fftype     = options.filetype?options.ftsetLineEnding(lineEnding[format])setFileType(type)unlesssetEncoding(encoding)detectEncoding()returntrueparseVimModeLine = (line) ->regexp = /(vi|vim|ex):\s*(se(t)*)*\s+([^:]+)*\s*:/matches = line.matchregexpoptions = nullifmatchesoptions = {}foroptioninmatches[4].split(" ")[key,value]=option.split"="options[key]=value?trueifkey!=""optionssetEncoding = (encoding) ->iconv = require'iconv-lite'editor = atom.workspace.getActiveTextEditor()returnfalseunlessiconv.encodingExists(encoding)encoding = encoding.toLowerCase().replace(/[^0-9a-z]|:\d{4}$/g,'')editor.setEncoding(encoding)returntruesetLineEnding = (lineEnding) ->editor = atom.workspace.getActiveTextEditor()buffer = editor?.getBuffer()returnunlessbufferbuffer.setPreferredLineEnding(lineEnding)buffer.setText(buffer.getText().replace(/\r\n|\r|\n/g,buffer.getPreferredLineEnding()))setFileType = (type) ->editor = atom.workspace.getActiveTextEditor()returnunlesseditorgrammar = atom.grammars.selectGrammar(type)ifgrammarisntatom.grammars.nullGrammaratom.grammars.setGrammarOverrideForPath(editor.getPath(),grammar)editor.setGrammar(grammar)detectEncoding = ->editor = atom.workspace.getActiveTextEditor()tryfilePath = editor.getPath()catcherrorreturnreturnunlessfs.existsSync(filePath)jschardet = require'jschardet'fs.readFilefilePath,(error, buffer) =>returniferror?{encoding}=jschardet.detect(buffer)?{}encoding = 'utf8'ifencodingis'ascii'setEncoding(encoding)

detectEncodingはencoding-selectorのdetectEncodingのうち、文字コード判定箇所のみを抜き出したものとなる。
モードラインによる判定に失敗した場合はfalseを返すようにしているので、モードラインそのものが設定されていなかったり、利用できない文字コードが設定されている場合はファイルの内容を読んで文字コードを判定している。
また、本来のモードラインの仕様であれば先頭または末尾の数行を読むようになっているが、この実装では先頭1行、末尾1行しか読んでいない。
これは筆者の環境では先頭または末尾にしか書いていないからなので、本来の実装に沿うのであればこの部分を書き換える必要がある。


Viewing all articles
Browse latest Browse all 5608

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>