改行を挿入すると、syntax-propertize-functionで強調表示が中断されるのはなぜですか?


6

三重引用符で囲まれた文字列を強調表示するメジャーモードを記述しようとしています。次に、最小限の再現可能な例を示します。

(defconst demo-triple-quoted-string-regex
  (rx "\"\"\""
      ;; After the delimiter, we're a sequence of
      ;; non-backslashes or blackslashes paired with something.
      (*? (or (not (any "\\"))
              (seq "\\" anything)))
      "\"\"\""))

(defun demo-stringify-triple-quote ()
  "Put `syntax-table' property on triple-quoted strings."
  (let* ((string-literal (match-string 0))
         (string-start-pos (- (point) (length string-literal)))
         (string-end-pos (point)))
    (unless (nth 4 (syntax-ppss)) ;; not inside comment
      (put-text-property string-start-pos string-end-pos
                         'syntax-table (string-to-syntax "|")))))

(defconst demo-syntax-propertize-function
  (syntax-propertize-rules
   (demo-triple-quoted-string-regex
    (0 (ignore (demo-stringify-triple-quote))))))

(define-derived-mode demo-mode prog-mode "Demo"
  "Major mode showing stack overflow question."
  (set (make-local-variable 'font-lock-defaults) '(()))
  (set (make-local-variable 'syntax-propertize-function)
       demo-syntax-propertize-function))

ただし、これはバッファを変更するときに本当に奇妙な動作につながります。これが私のバッファの内容です:

dodgy when we put a newline after babel

"""
a
"
babel

"""

x = 1

M-x demo-mode 正しい強調表示を与える:

デモビフォー

しかし、Enterキーを押すと、突然次のようになります。

デモ後

何が悪いのですか?


私には解決策はありませんが、org-modeの行の折り返しでイタリックと太字が続くという同じ問題に気づきました。
Emacsユーザー

1
最初の問題は、構文を文字列のすべての文字に配置することですが、構文は最初と最後のフェンス文字に対してのみ実行する必要があります。Emacsがを介して、想定される文字列のすべてのペアを1つのsexp、つまり単一の文字列として扱うことを確認できますforward-sexp
politza

1
2番目の問題は、想像したとおりに文字列を実際に一致させることができないことです。これは、バッファに既に存在する文字列の外側から検索が開始されることが保証されている場合にのみ機能します。結局のところ、ペアを一致させています。1つのトリプルは、偶数のその他のトリプルが前にある場合に限り、文字列を開始します。幸いにもこれsyntax-ppssを追跡します。それがでどのように行われるかを見てくださいpython.el
politza

@politza私はあなたのelispスキルに畏敬の念を抱いています!本当にありがとう :)。あなたの修正は私のコードを動作させるのに十分でした(以下の回答を参照)ので、これに私を巻き込んだジュリアモードのバグを修正することができます
Wilfred Hughes

回答:


3

Politzaのおかげで、スルーステッピングpython-syntax-stringifyEdebugので、私はこの作業を持っています。変更点は次のとおりです。

  • | 三重引用符で囲まれた文字列の最初と最後の文字にのみ適用する必要があります。
  • (構文解析はインクリメンタルなので?)文字列全体を検索することはできません。代わりに、区切り文字を検索し、三重引用符で囲まれた文字列になっているかどうかを確認します。

作業コード:

(defconst demo-triple-quoted-string-regex
  (rx "\"\"\""))

(defun demo-stringify-triple-quote ()
  "Put `syntax-table' property on triple-quoted strings."
  (let* ((string-end-pos (point))
         (string-start-pos (- string-end-pos 3))
         (ppss (prog2
                   (backward-char 3)
                   (syntax-ppss)
                 (forward-char 3))))
    (unless (nth 4 (syntax-ppss)) ;; not inside comment
      (if (nth 8 (syntax-ppss))
          ;; We're in a string, so this must be the closing triple-quote.
          ;; Put | on the last " character.
          (put-text-property (1- string-end-pos) string-end-pos
                             'syntax-table (string-to-syntax "|"))
        ;; We're not in a string, so this is the opening triple-quote.
        ;; Put | on the first " character.
        (put-text-property string-start-pos (1+ string-start-pos)
                           'syntax-table (string-to-syntax "|"))))))

(defconst demo-syntax-propertize-function
  (syntax-propertize-rules
   (demo-triple-quoted-string-regex
    (0 (ignore (demo-stringify-triple-quote))))))

(define-derived-mode demo-mode prog-mode "Demo"
  "Major mode showing stack overflow question."
  (set (make-local-variable 'font-lock-defaults) '(()))
  (set (make-local-variable 'syntax-propertize-function)
       demo-syntax-propertize-function))

で計算ppssしてlet*、その値を使用しないのはなぜですか?
Michael Norrish
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.