Honmushi blog
2019/08/01

Emmet-vimでReactNative用のsnippetを作成

React Nativeでは表示についてcssに似た記法の「Style」で指定することが出来ます。 しかし、ReactNativeのStyleはキャメルケースで記述しないといけません。

例えば以下のようになります。

const styles = StyleSheet.create({
  inputName: {
    borderWidth: 1,
    borderColor: '#DDD',
    width: "80%",
    marginBottom: 20,
  },
})

これ書くのがいつも面倒だったので、なんとか出来ないかやってみました。 ちなみに私は開発ではVimを使っています。UbuntuでVimです。 もともとcssなんかを書く際にはEmmet-vimを使っていたので、これをカスタマイズして使うのがいいかなと思いました。

結論

Emmet-vimにオリジナルのsnippetを追加することで解決しました。

軽く調べた所、VSCodeで使えるプラグイン?で以下を見つけました。
react-native-emmet

まさにこれがやりたいのですが、vimには対応していませんでした。しかし、このプラグインのsnippetの内容が公開されていたので、それを参考にして自分用のsnippetを作成しました。
react-native-emmetのsnippetの内容

概要

  • カスタムsnippetファイルを作る
  • vimrcに設定ファイルを記載

カスタムsnippetファイルを作る

今回のReactNative用のsnippetを作成しました。snippets_custom.jsonなどの名前で保存します。 Emmetのサイトにカスタマイズの書き方など載っているので参考にするといいです。

今回は以下のVSCodeのプラグインの定義を見つけたので、そちらを書き直して作成しました。
react-native-emmetのsnippetの内容

以下snippets_custom.jsonの内容です。

{
  "javascript": {
    "filters": "html",
    "profile": "javascript",
    "snippets": {
      "create": "const styles = StyleSheet.create({})",
      "pselect": "...Platform.select({ ios: {}, android: {}})",
      "sdw": "...Platform.select({ ios: { ...shadowProperties }, android: { elevation }}),",
      "v": "<View style={{}}></View>",
      "te": "<Text style={{}}></Text>",
      "v>te": "<View><Text></Text></View>",
      "img": "<Image source={{}} style={{}} />",
      "fl":  "<FlatList data={} renderItem={} />",
      "sl": "<SectionList data={} renderItem={} />",
      "sv": "<ScrollView></ScrollView>",
      "ti": "<TextInput value={} placeholder='' underlineColorAndroid={false} onChange={} />",
      "mdl": "<Modal visible={} animation='slide'></Modal>",
      "to": "<TouchableOpacity style={} onPress={}></TouchableOpacity>",
      "twf": "<TouchableWidthoutFeedback style={} onPress={}></TouchableWidthoutFeedback>",
      "th": "<TouchableHighlight style={{}} onPress={}></TouchableHighlight>",
      "twnf": "<TouchableWithNativeFeedback style={} onPress={}></TouchableWithNativeFeedback>",
      "actind": "<ActivityIndicator color='#00ff00' />",
      "btn": "<Button color='#00ff00' title='button' onPress={} />",
      "pos": "position: 'absolute',",
      "pos:a": "position: 'absolute',",
      "pos:r": "position: 'relative',",
      "t": "top: 0,",
      "t:a": "top: 'auto',",
      "r": "right: 0,",
      "r:a": "right: 'auto',",
      "b": "bottom: 0,",
      "b:a": "bottom: 'auto',",
      "l": "left: 0,",
      "l:a": "left: 'auto',",
      "z": "zIndex: 2,",
      "ov": "overflow: 'hidden',",
      "ov:v": "overflow: 'visible',",
      "ov:h": "overflow: 'hidden',",
      "m": "margin: 4,",
      "m:a": "margin: 'auto',",
      "mt": "marginTop: 4,",
      "mt:a": "marginTop: 'auto',",
      "mr": "marginRight: 4,",
      "mr:a": "marginRight: 'auto',",
      "mb": "marginBottom: 4,",
      "mb:a": "marginBottom: 'auto',",
      "ml": "marginLeft: 4,",
      "ml:a": "marginLeft: 'auto',",
      "p": "padding: 4,",
      "pt": "paddingTop: 4,",
      "pr": "paddingRight: 4,",
      "pb": "paddingBottom: 4,",
      "pl": "paddingLeft: 4,",
      "w": "width: 120,",
      "w:a": "width: 'auto',",
      "h": "height: 120,",
      "h:a": "height: 'auto',",
      "maxw": "maxWidth: 120,",
      "maxh": "maxHeight: 120,",
      "minw": "minWidth: 120,",
      "minh": "minHeight: 120,",
      "bc": "borderColor: '#000',",
      "bc:t": "borderColor: 'transparent',",
      "bw": "borderWidth: 1,",
      "btw": "borderTopWidth: 1,",
      "brw": "borderRightWidth: 1,",
      "bbw": "borderBottomWidth: 1,",
      "blw": "borderLeftWidth: 1,",
      "btc": "borderTopColor: '#000',",
      "btc:t": "borderTopColor: 'transparent',",
      "brc": "borderRightColor: '#000',",
      "brc:t": "borderRightColor: 'transparent',",
      "bbc": "borderBottomColor: '#000',",
      "bbc:t": "borderBottomColor: 'transparent',",
      "blc": "borderLeftColor: '#000',",
      "blc:t": "borderLeftColor: 'transparent',",
      "br": "borderRadius: 4,",
      "bgc": "backgroundColor: '#fff',",
      "bgc:t": "backgroundColor: 'transparent',",
      "c": "color: '#333',",
      "c:r": "color: 'rgb(0, 0, 0)',",
      "c:ra": "color: 'rgba(0, 0, 0, 0.5)',",
      "ta": "textAlign: 'center',",
      "ta:l": "textAlign: 'left',",
      "ta:c": "textAlign: 'center',",
      "ta:r": "textAlign: 'right',",
      "ta:j": "textAlign: 'justify',",
      "td": "textDecoration: 'underline',",
      "t:s": "transform: { scale: 1.2 }",
      "t:sx": "transform: { scaleX: 1.2 }",
      "t:sy": "transform: { scaleY: 1.2 }",
      "t:r": "transform: { rotate: '90deg' }",
      "t:rx": "transform: { rotateX: '90deg' }",
      "t:ry": "transform: { rotateY: '90deg' }",
      "t:rz": "transform: { rotateZ: '90deg' }",
      "t:tx": "transform: { translateX: x }",
      "t:ty": "transform: { translateY: y }",
      "lh": "lineHeight: 24,",
      "ls": "letterSpacing: 1,",
      "fw": "fontWeight: 600,",
      "fw:b": "fontWeight: 'bold',",
      "fst": "fontStyle: 'italic',",
      "fst:i": "fontStyle: 'italic',",
      "fs": "fontSize: 14,",
      "ff": "fontFamily: 'Roboto'},",
      "o": "opacity: 0.5,",
      "fd": "flexDirection: 'row',",
      "fd:r": "flexDirection: 'row',",
      "fd:rr": "flexDirection: 'row-reverse',",
      "fd:c": "flexDirection: 'column',",
      "fd:cr": "flexDirection: 'column-reverse',",
      "fwrap": "flexWrap: 'wrap',",
      "fwrap:n": "flexWrap: 'nowrap',",
      "fwrap:w": "flexWrap: 'wrap',",
      "jc": "justifyContent: 'center',",
      "jc:fs": "justifyContent: 'flex-start',",
      "jc:fe": "justifyContent: 'flex-end',",
      "jc:c": "justifyContent: 'center',",
      "jc:se": "justifyContent: 'center',",
      "jc:sb": "justifyContent: 'space-between',",
      "jc:sa": "justifyContent: 'space-around',",
      "ai": "alignItems: 'center',",
      "ai:fs": "alignItems: 'flex-start',",
      "ai:fe": "alignItems: 'flex-end',",
      "ai:c": "alignItems: 'center',",
      "ai:s": "alignItems: 'stretch',",
      "f": "flex: 1,",
      "as": "alignSelf: 'center',",
      "mh": "marginHorizontal: 16,",
      "mv": "marginVertical: 16,",
      "bfv:v": "backfaceVisibility: 'visible',",
      "bblr": "borderBottomLeftRadius: 4,",
      "bbrr": "borderBottomRightRadius: 4,",
      "btlr": "borderTopLeftRadius: 4,",
      "btrr": "borderTopRightRadius: 4,",
      "rm": "resizeMode: 'cover',",
      "ovclr": "overlayColor: '#fff',",
      "bs": "borderStyle: 'dashed',",
      "elv": "elevation: 4",
      "tsc": "textShadowColor: '#000',",
      "tso": "textShadowOffset: { width: 0, height: 4 },",
      "tsr": "textShadowRadius:  2,",
      "tav": "textAlignVertical: 'center',"
    }
  }
}

vimrcに設定ファイルを記載

Emmet-vimのドキュメントにもありますが、オリジナルの設定を追加出来ます。
Emmet-vim

.vimrcに上記で作成したカスタムsnippetファイルを指定します。

let g:user_emmet_settings = webapi#json#decode(join(readfile(expand('~/.vim/custom/snippets_custom.json')), "\n"))

web-apiを有効にする

JSONパーサーを含んでいるweb-apiというプラグインが必要です。
無い場合は用意しておきましょう。Emmet-vimと同様にVimにインストールすればいいです。

おわりに

これでReactNativeのStyle定義のEmmetが動作するようになりました。ファイルが.jsすなわちJavaScriptの場合はこのsnippetが適応されるようになっています。

細かいところは改良が出来ますし、他の拡張子に対しての設定や追加・継承なんかも出来ます。

そういえばneosnippet.vimを使うという手段もあったんですが、どっちが良かったんでしょうかね。 今のところはReact Nativeの記法はEmmet-vimで、JavaScriptの基本構文はneosnippetという棲み分けになってます。 私はそれぞれ<C-e><C-k>に割り当ててます。

  • このエントリーをはてなブックマークに追加