シンタックスハイライト

このプラグインは、正確でリッチな色分けを実現するために、2つのレイヤーのシンタックスハイライトを提供します。レイヤー1(レクサーベース)は、ファイルを開いた瞬間に外部依存なしで即座にハイライトを行います。レイヤー2(セマンティック)は、Language Server が接続されている場合に型情報に基づいた色分けをオーバーレイし、両方の利点を活かします。

レイヤー1: レクサーベースのハイライト

Native

組み込みの JFlex レクサーが ReScript ソースコードをトークンに分解し、トークンの種類に基づいて色を適用します。これは外部依存なしで即座に動作します。Language Server が利用できない場合(例:プロジェクトの初期セットアップ中や ReScript プロジェクト外のファイル)でも、レクサーベースのハイライトによりコードは完全に色分けされます。

ハイライト対象の要素

要素

キーワード

let, type, module, switch, if, else, async, await

キーワード演算子

mod, land, lor, lxor, lsl, lsr, asr

文字列

"hello", `template ${x}`, 'c'

数値

42, 3.14, 0xFF, 0b1010

コメント

// line comment, /* block comment */

演算子

+, -, =>, ->, |>, ==, ===

デコレーター

@module, @send, @genType

型パラメーター

'a, 'b

ポリモーフィックバリアント

#Red, #Blue

モジュール名

Belt, Array, React

以下のセクションでは、各要素カテゴリをコード例とともに詳しく説明します。

キーワード

ReScript のキーワードは、プログラムフローの制御、バインディングの宣言、構造の定義を行います。これらは Keyword カラー(通常は太字または目立つ色合い)で表示されます。

宣言キーワード は新しい名前を導入します:

let message = "hello"
type color = Red | Green | Blue
module Utils = {
  let add = (a, b) => a + b
}
external readFile: string => string = "readFileSync"
exception NotFound(string)

制御フローキーワード は実行パスを決定します:

if condition {
  doSomething()
} else {
  doOther()
}

switch value {
| Some(x) => x
| None => default
}

for i in 0 to 10 {
  Console.log(i)
}

while running {
  process()
}

try {
  riskyOperation()
} catch {
| Exn.Error(e) => handleError(e)
}

その他のキーワード には以下が含まれます:

  • モジュールアクセスのための openinclude

  • 非同期プログラミングのための asyncawait

  • 再帰バインディング制御のための recnonrec

  • ミュータブルレコードフィールドのための mutable

  • パターンマッチングの詳細化のための as, with, when

  • 遅延評価のための lazy

  • 実行時アサーションのための assert

open Belt

let rec fibonacci = (n) =>
  switch n {
  | 0 | 1 => n
  | n => fibonacci(n - 1) + fibonacci(n - 2)
  }

let result = await fetchData()

キーワード演算子

キーワード演算子 mod, land, lor, lxor, lsl, lsr, asr は整数算術演算およびビット演算であり、通常の演算子カラーではなく キーワードスタイルの色 で表示されます。これは、他の ML 系言語と同様に、演算子として予約されたアルファベット識別子であるためです。

演算子

意味

mod

整数の剰余(余り)

land

ビット AND

lor

ビット OR

lxor

ビット XOR

lsl

論理左シフト

lsr

論理右シフト

asr

算術右シフト(符号保持)

let remainder = 17 mod 5       // 2
let masked = flags land 0xFF   // bitwise AND with mask
let combined = a lor b         // bitwise OR
let flipped = a lxor b         // bitwise XOR
let shifted = 1 lsl 8          // 256 (left shift)
let halved = 256 lsr 1         // 128 (right shift)
let signAware = -128 asr 1     // -64 (arithmetic shift)

文字列

レクサーは3種類の文字列リテラルを認識し、すべて String カラーで表示します:

ダブルクォート文字列 は最も一般的な形式です:

let name = "ReScript"
let withEscape = "line1\nline2\ttab"
let withUnicode = "Hello \u0041"  // "Hello A"
let withQuote = "She said \"hello\""

テンプレートリテラル (バッククォート文字列)は ${} による補間をサポートします。文字列の内容は文字列として色分けされ、${} 内の補間式はそれぞれ独自のシンタックスハイライトが適用されます:

let greeting = `Hello, ${name}!`
let multiline = `
  Name: ${user.name}
  Age: ${user.age->Int.toString}
  Score: ${(score *. 100.0)->Float.toString}%
`

文字リテラル はシングルクォートを使用し、正確に1文字を含みます:

let letter = 'A'
let newline = '\n'
let tab = '\t'

数値

すべての数値リテラルは Number カラーで表示されます。ReScript はいくつかの数値形式をサポートしています:

各種基数による 整数リテラル:

let decimal = 42
let negative = -17
let hex = 0xFF          // 255 in hexadecimal
let octal = 0o77        // 63 in octal
let binary = 0b1010     // 10 in binary
let bigDecimal = 1_000_000  // underscores for readability

小数点または科学的記法による 浮動小数点リテラル:

let pi = 3.14159
let negative = -2.5
let scientific = 1.0e10
let small = 6.022e-23

コメント

ReScript は2種類のコメントスタイルをサポートしており、それぞれ独自のカラー属性を持ちます:

行コメント// で始まり、行末まで続きます。Line comment カラーが使用されます:

// This is a line comment
let x = 42 // inline comment

ブロックコメント/**/ で囲みます。Block comment カラーが使用されます:

/* This is a block comment */

/*
 * Multi-line block comment
 * often used for documentation
 */
let important = true

ネストされたコメント

多くの言語とは異なり、ReScript はネストされたブロックコメントをサポートしています。レクサーはネストの深さを正しく追跡するため、すでにブロックコメントを含むコードをコメントアウトすることができます:

/* outer comment
  /* inner comment is fine */
  still inside the outer comment
*/

これは、開発中にコードの大きなセクションを一時的にコメントアウトする際に特に便利です。

演算子

演算子は Operator カラーで表示されます。レクサーは幅広い演算子を認識します:

算術演算子:

let sum = a + b
let diff = a - b
let product = a * b
let quotient = a / b
let fSum = a +. b      // float addition
let fDiff = a -. b     // float subtraction
let fProd = a *. b     // float multiplication
let fQuot = a /. b     // float division

比較演算子:

let equal = a == b         // structural equality
let strictEqual = a === b  // referential equality
let notEqual = a != b
let strictNotEqual = a !== b
let less = a < b
let lessEq = a <= b
let greater = a > b

論理演算子:

let both = a && b
let either = a || b

パイプ演算子とアロー演算子 は ReScript の最も特徴的な演算子です:

// Pipe forward — data-first composition
let result = data
  |> Array.map(transform)
  |> Array.filter(isValid)

// Fat arrow — function body
let add = (a, b) => a + b

// Thin arrow — type annotation for functions
type callback = int -> string

// String concatenation
let full = first ++ " " ++ last

その他の演算子:

let spread = {...record, name: "new"}  // spread operator (...)
let ref = contents := newValue         // ref assignment (:=)

デコレーター

デコレーター(アノテーション)は @ で始まり、Annotation カラーで表示されます。ReScript では FFI(Foreign Function Interface)バインディングやコンパイラディレクティブに多用されます:

JavaScript 連携のための FFI デコレーター:

@module("fs")
external readFileSync: string => string = "readFileSync"

@send
external push: (array<'a>, 'a) => unit = "push"

@val
external document: Dom.document = "document"

@get
external innerHTML: Dom.element => string = "innerHTML"

@set
external setInnerHTML: (Dom.element, string) => unit = "innerHTML"

@new
external createDate: unit => Js.Date.t = "Date"

@scope("Math") @val
external random: unit => float = "random"

コンパイラデコレーター:

@genType
let add = (a, b) => a + b

@dead
let unusedHelper = () => ()

@inline
let maxSize = 100

@unboxed
type stringOrNumber = String(string) | Number(float)

型パラメーター

型パラメーター(ジェネリクス)である 'a, 'b, 'key, 'valueType argument カラーで表示され、通常はメタデータ/アノテーションスタイルが使用されます。これらはパラメトリック多相性を表し、型や関数が任意の型で動作することを可能にします:

type option<'a> = Some('a) | None

type result<'ok, 'err> = Ok('ok) | Error('err)

let first: array<'a> => option<'a> = arr =>
  if Array.length(arr) > 0 {
    Some(arr[0])
  } else {
    None
  }

type map<'key, 'value> = {
  get: 'key => option<'value>,
  set: ('key, 'value) => unit,
}

ポリモーフィックバリアント

ポリモーフィックバリアントは # で始まり、Polymorphic variant カラー(通常は定数に近いスタイル)で表示されます。type 宣言で定義する必要がある通常のバリアントとは異なり、ポリモーフィックバリアントはアドホックに使用できます:

let color = #Red
let status = #"needs-review"   // string polymorphic variant

let describe = (c) =>
  switch c {
  | #Red => "warm"
  | #Blue => "cool"
  | #Green => "natural"
  }

// Polymorphic variants work across module boundaries without type definitions
let handleResponse = (status) =>
  switch status {
  | #ok => Console.log("Success")
  | #error => Console.log("Failed")
  | #pending => Console.log("Waiting...")
  }

通常のバリアントとの主な違いは、ポリモーフィックバリアントは事前の型宣言を必要とせず、関連のない型やモジュール間で共有できることです。

レクサーベースのハイライトは即座にオフラインで動作します — Language Server が起動する前や ReScript プロジェクト外のファイルでも、キーワード、文字列、コメント、演算子を正確に認識してコードが完全にカラーリングされます。

モジュール名

大文字で始まる識別子は Module name(通常はクラス名カラー)で色分けされます。ReScript では、大文字の識別子は慣例的にモジュール、バリアントコンストラクター、ファンクターを表します:

open Belt.Array

module StringMap = Map.Make(String)

let data = Array.map([1, 2, 3], x => x * 2)
let encoded = Js.Json.stringify(value)
let element = React.string("hello")

注釈

レクサーレベルでは、すべての大文字識別子にモジュール名カラーが適用されます。Language Server が接続されている場合、セマンティックハイライトにより、実際のモジュール(namespace トークンタイプ)とバリアントコンストラクター(enumMember トークンタイプ)を区別して、より精密な色分けが行われます。

JSX / React コンポーネントのハイライト

Native

ReScript はファーストクラスの JSX サポートを備えており、レクサーは JSX 要素に専用のトークンタイプと色分けを提供します。プラグインは ReScript ファイル内の JSX 構造を認識し、専門的なハイライトを適用します。

JSX タグのハイライト

JSX タグは通常の ReScript コードとは異なる色で表示されます。プラグインは以下を区別します:

  • HTML 要素<div>, <span> のような小文字タグ)-- JSX tag カラーで表示

  • React コンポーネント<App>, <Header> のような大文字タグ)-- コンポーネントモジュールとしての役割に合わせて Module name カラーで表示

// HTML elements — use JSX tag color
let page = <div className="container">
  <h1> {React.string("Title")} </h1>
  <p> {React.string("Content")} </p>
</div>

// React components — use module name color
let app = <Layout>
  <Header title="My App" />
  <Sidebar items={menuItems} />
  <MainContent>
    {children}
  </MainContent>
</Layout>

JSX ブラケットの色分け

JSX ブラケット(<, >, </, />)は JSX tag bracket カラーで表示され、比較演算子やその他の山括弧の使用と視覚的に区別されます:

// Opening tag brackets: < and >
<div className="app">

// Closing tag brackets: </ and >
</div>

// Self-closing bracket: />
<Component prop="value" />

Props のハイライト

JSX の props は通常の ReScript 識別子および式としてハイライトされます。名前付き props、省略記法の props、オプショナル props にはすべて適切な色分けが適用されます:

let button = <Button
  onClick={handleClick}           // expression prop
  disabled={true}                 // boolean prop
  className="primary"             // string prop
  size={#large}                   // polymorphic variant prop
  ?tooltip                        // optional punned prop
/>

式の補間

JSX の子要素内の式は {} で囲まれ、中括弧内では通常の ReScript シンタックスハイライトが適用されます:

let greeting = <div>
  {React.string("Hello, " ++ name ++ "!")}
  {React.int(count)}
  {items
    ->Array.map(item => <li key={item.id}> {React.string(item.name)} </li>)
    ->React.array}
</div>

React コンポーネントの完全な例

以下は、JSX ハイライトの全範囲を示す完全な React コンポーネントの例です:

@react.component
let make = (~title: string, ~items: array<item>, ~onSelect: item => unit) => {
  let (selected, setSelected) = React.useState(() => None)

  let handleClick = (item, _event) => {
    setSelected(_ => Some(item))
    onSelect(item)
  }

  <div className="item-list">
    <h2> {React.string(title)} </h2>
    <ul>
      {items
        ->Array.map(item => {
          let isActive = selected == Some(item)
          <li
            key={item.id}
            className={isActive ? "active" : ""}
            onClick={handleClick(item)}
          >
            <span className="name"> {React.string(item.name)} </span>
            <span className="price"> {React.float(item.price)} </span>
          </li>
        })
        ->React.array}
    </ul>
    {switch selected {
    | Some(item) => <p> {React.string(`Selected: ${item.name}`)} </p>
    | None => <p className="hint"> {React.string("Click an item")} </p>
    }}
  </div>
}

この例では:

  • <div>, <h2>, <ul>, <li>, <span>, <p>JSX タグ(HTML 要素)として色分け

  • < > </ /> ブラケットは JSX タグブラケット として色分け

  • className, key, onClick は通常の識別子としてハイライト

  • {} 内の式には完全な ReScript シンタックスハイライトが適用

  • React.string, React.float, React.array では Reactモジュール名 の色分けを使用

専用の JSX ハイライトによりマークアップ構造とロジックが視覚的に分離され、HTML 要素、コンポーネント参照、式の補間を一目で区別できるため、React コンポーネントコードがスキャンしやすくなります。

レイヤー2: セマンティックハイライト

LSP Required

Language Server が接続されている場合、セマンティックトークンはコンパイラからの実際の型情報に基づいて追加のハイライトレイヤーを提供します。これにより、各識別子の意味を理解した正確でコンテキストを考慮した色分けが実現されます。

セマンティックトークンの種類

トークンタイプ

意味

variable

変数とパラメーター

let x = 1 -- x が variable として色分け

type

型名

type t = int -- t が type として色分け

namespace

モジュール名

Belt.Array -- Belt が namespace として色分け

enumMember

バリアントコンストラクター

Some(x) -- Some が enum member として色分け

property

レコードフィールド

user.name -- name が property として色分け

interface

JSX の HTML 要素

<div> -- div が interface として色分け

operator

演算子

+, |> が operator として色分け

modifier

JSX ブラケット

<, /> が modifier として色分け

セマンティックトークンの仕組み

ReScript ファイルを開き Language Server が実行中の場合、プラグインはドキュメント全体のセマンティックトークンをリクエストします。サーバーはコンパイル済みプログラムを分析し、トークンとその型のリストを返します。プラグインはこれらのセマンティックカラーをレクサーベースのハイライトの上にオーバーレイします。

このプロセスはバックグラウンドで自動的に行われ、ファイルを編集するたびに更新されます。

トークンタイプの詳細説明

変数 (variable)

variable トークンタイプは、ローカルバインディング、関数パラメーター、その他の値レベルの識別子に適用されます。これにより、値の名前を型名、モジュール名、キーワードと区別できます:

let count = 42              // "count" → variable
let add = (a, b) => a + b  // "add", "a", "b" → variable
let name = user.name        // "name" (binding) → variable

セマンティックハイライトがない場合、これらの識別子はレクサーから特別な色を受けません(小文字の識別子はデフォルトではスタイルなし)。セマンティックトークンにより、明確な Variable カラーが付与されます。

型 (type)

type トークンは、定義、アノテーション、型式など、型名が出現するすべての場所に適用されます:

type color = Red | Green | Blue   // "color" → type
let x: int = 42                   // "int" → type
type user = {name: string}        // "user", "string" → type

これにより、型レベルのコードが値レベルのコードと視覚的に区別され、複雑な型定義の可読性が向上します。

名前空間 (namespace)

namespace トークンは、モジュール名が修飾子として使用される場合に適用されます。これにより、レクサーの広範な「大文字識別子」の色分けが、既知のモジュールを特定的にマークすることで精緻化されます:

Belt.Array.map(data, fn)     // "Belt", "Array" → namespace
Js.Json.stringify(value)     // "Js", "Json" → namespace
React.useState(() => None)   // "React" → namespace

列挙型メンバー (enumMember)

バリアントコンストラクターと例外名は enumMember トークンを受け取り、どちらも大文字識別子を使用するにもかかわらず、モジュール名と区別されます:

let result = Some("value")      // "Some" → enumMember
let empty = None                // "None" → enumMember
let color = Red                 // "Red" → enumMember
raise(NotFound("missing"))     // "NotFound" → enumMember

これはセマンティックハイライトの最も価値ある改善の一つです。Language Server がない場合、Some, None, RedBelt のようなモジュール名はすべてレクサーの同じモジュール名カラーを共有します。セマンティックトークンにより、これらを視覚的に区別できるようになります。

プロパティ (property)

レコードフィールド名は、アクセスまたは定義される際に property トークンを受け取ります:

type user = {
  name: string,    // "name" → property
  age: int,        // "age" → property
}

let userName = user.name    // "name" (access) → property
let updated = {...user, age: 30}  // "age" → property

インターフェース (interface)

JSX 内の HTML 要素名は interface トークンを受け取ります。これにより、React コンポーネント名(namespace トークンを受け取る)とは異なる色が付与されます:

<div className="app">       // "div" → interface
  <span> {text} </span>     // "span" → interface
  <input type_="text" />    // "input" → interface
</div>

演算子 (operator)

セマンティック operator トークンは、型を考慮した演算子ハイライトを提供し、解決された意味に基づいて演算子を区別できます。これはレクサーの構文的な演算子の色分けを補完します:

let sum = a + b     // "+" → operator
let piped = data |> transform  // "|>" → operator
let neg = !flag     // "!" → operator

修飾子 (modifier)

JSX ブラケットトークン(<, >, </, />)は Language Server から modifier トークンを受け取り、明確なセマンティックカラーを持つことができます:

<div>       // "<", ">" → modifier
</div>      // "</", ">" → modifier
<br />      // "<", "/>" → modifier

セマンティックオーバーライドの動作

セマンティックハイライトはレクサーハイライトの上にオーバーレイされます。両方のレイヤーが同じトークンに色を割り当てた場合、セマンティックカラーが優先されます。セマンティックトークンが提供する主な改善点は以下のとおりです:

識別子

レクサーカラー

セマンティックカラー

利点

Belt(モジュール)

モジュール名

名前空間

モジュールの使用を確認

Some(コンストラクター)

モジュール名

列挙型メンバー

モジュールとの区別

name(フィールド)

色なし

プロパティ

レコードアクセスのハイライト

count(変数)

色なし

変数

値バインディングのハイライト

int(型)

色なし

型アノテーションのハイライト

div(JSX)

JSX タグ

インターフェース

セマンティック確認

フォールバック動作

Language Server が切断されているか利用できない場合、プラグインはレクサーのみのハイライトにグレースフルにフォールバックします:

  • すべてのキーワード、文字列、数値、コメント、演算子のハイライトは引き続き正常に動作します

  • モジュール名、型パラメーター、ポリモーフィックバリアント、デコレーターはレクサーベースの色を保持します

  • 小文字識別子(変数、関数名、レコードフィールド)はセマンティックカラーを失い、デフォルトのテキストカラーで表示されます

  • JSX 要素はレクサーベースのタグの色分けを保持します

接続状態と切断状態の間の遷移はシームレスです。Language Server が再接続されるとすぐに、セマンティックトークンが再リクエストされ、色分けが復元されます。

セマンティックハイライトはレクサーベースのハイライトでは解決できない曖昧さを解消します — 例えば、Some のようなバリアントコンストラクタと Belt のようなモジュール名を、どちらも大文字識別子であるにもかかわらず視覚的に区別し、コードの可読性を大幅に向上させます。

参考

高度な機能 では、LSP セマンティック情報を使用する Code Lens やインレイヒントについて説明しています。

色のカスタマイズ

Native

SettingsEditorColor SchemeReScript で各トークンタイプの色をカスタマイズできます。

プラグインには Darcula(ダーク)と Default(ライト)の両テーマ用に最適化されたカラースキームが同梱されています。

利用可能なカラー属性

設定ページは以下のグループに整理されています:

基本要素:

属性

説明

Keyword

let, type, switch およびその他すべてのキーワード

String

文字列リテラル、テンプレート文字列、文字リテラル

Number

整数リテラルと浮動小数点リテラル

演算子

算術演算子、比較演算子、パイプ演算子、アロー演算子

Annotation

デコレーター属性(@module, @genType など)

Type argument

型パラメーター('a, 'b

Polymorphic variant

ポリモーフィックバリアントタグ(#Red, #ok

モジュール名

大文字識別子(モジュール、ファンクター)

コメント:

属性

説明

Line comment

単一行コメント(//

Block comment

複数行コメント(/* */

括弧と演算子:

属性

説明

Braces

{}

Brackets

[]

Parentheses

()

Dot

. アクセサー

Comma

, セパレーター

Semicolon

; ターミネーター

パターンマッチング:

属性

説明

Pipe (|)

パターンマッチのアーム区切り

Wildcard (_)

キャッチオールパターン

JSX:

属性

説明

JSX タグ

HTML 要素名(div, span

JSX tag bracket

タグ区切り文字(<, >, </, />

セマンティック(LSP):

属性

説明

変数

変数と関数パラメーター

定義とアノテーション内の型名

Namespace(モジュール)

修飾子として使用されるモジュール名

Enum member(バリアント)

バリアントコンストラクター(Some, None, Red

Property(レコードフィールド)

レコードフィールド名

Interface(JSX タグ)

JSX 内の HTML 要素名(セマンティック)

演算子

演算子(セマンティック)

Modifier(JSX ブラケット)

JSX ブラケット(セマンティック)

ヒント

  • カラー設定ページの Preview ペインには、レクサーとセマンティックの両方のハイライトが適用された ReScript コードサンプルがライブ表示されるため、変更の効果をすぐに確認できます。

  • カスタムカラースキームを使用する場合、プラグインの Darcula および Default 用デフォルト設定が良い出発点になります。

  • セマンティックカラー属性は Language Server が接続されている場合にのみ有効になります。レクサーベースの対応する属性とは独立してカスタマイズできます。

完全なカラーカスタマイズにより、ReScript のハイライトを個人の好みやチームの規約に合わせて調整でき、ライブプレビューで変更をすぐに確認できます。