|
| 1 | +;;; mbedtls-test-data-mode.el --- mode for Mbed TLS test data files |
| 2 | + |
| 3 | +;;; Code: |
| 4 | + |
| 5 | +(defvar mbedtls-test-data-mode-syntax-table |
| 6 | + (let ((table (make-syntax-table))) |
| 7 | + (modify-syntax-entry ?\n ">" table) |
| 8 | + (modify-syntax-entry ?\" "\"" table) |
| 9 | + table) |
| 10 | + "Syntax table to use in Mbed TLS test data mode.") |
| 11 | + |
| 12 | +(defvar mbedtls-test-data-functions nil |
| 13 | + "List of test functions available for this data file. |
| 14 | +Each element of the list has the form (NAME ARGUMENTS). |
| 15 | +NAME is the name of the test function. |
| 16 | +ARGUMENTS is a vector of argument names.") |
| 17 | +(make-variable-buffer-local 'mbedtls-test-data-functions) |
| 18 | + |
| 19 | +(defvar mbedtls-test-data-function-buffer nil |
| 20 | + "Buffer visiting the associated .function file.") |
| 21 | +(make-variable-buffer-local 'mbedtls-test-data-function-buffer) |
| 22 | + |
| 23 | +(defun mbedtls-test-data-function-file-name (&optional data-file) |
| 24 | + "Return the name of the .function file associated with the current .data file. |
| 25 | +With no argument or with nil as an argument, use the current buffer's file name. |
| 26 | +If the optional argument DATA-FILE is a buffer, use its buffer file name. |
| 27 | +If the optional argument DATA-FILE is a string, use that as the name of the |
| 28 | +.data file." |
| 29 | + (let ((file-name (if (stringp data-file) |
| 30 | + data-file |
| 31 | + (buffer-file-name data-file))) |
| 32 | + (case-fold-search t)) |
| 33 | + (save-match-data |
| 34 | + (if (string-match "\\(\\.[-0-9A-Z_a-z]+\\)?\\.data\\'" file-name) |
| 35 | + (setq file-name (substring file-name 0 (match-beginning 0))))) |
| 36 | + (concat file-name ".function"))) |
| 37 | + |
| 38 | +(defun mbedtls-test-data-visit-function-file (&optional data-file |
| 39 | + no-error |
| 40 | + select) |
| 41 | + "Visit the .function file associated with the current .data file. |
| 42 | +Return the buffer visiting the file. |
| 43 | +Interpret optional argument DATA-FILE as `mbedtls-test-data-visit-function-file'. |
| 44 | +If the optional argument NO-ERROR is non-nil, don't error out if the file |
| 45 | +cannot be visited, just return nil. |
| 46 | +If the optional argument SELECT is non-nil, display the buffer. This is the |
| 47 | +default when this function is called interactively." |
| 48 | + (interactive '(nil nil t)) |
| 49 | + (let ((filename (mbedtls-test-data-function-file-name data-file))) |
| 50 | + (cond |
| 51 | + (select (find-file filename)) |
| 52 | + (no-error (condition-case nil |
| 53 | + (find-file-noselect filename t) |
| 54 | + (error nil))) |
| 55 | + (t (find-file-noselect filename t))))) |
| 56 | + |
| 57 | +(defun mbedtls-test-data-parse-function-argument-name (function-data limit) |
| 58 | + (let ((found nil)) |
| 59 | + (while (and (< limit (point)) |
| 60 | + (not (setq found (looking-at "[0-9A-Z_a-z]+")))) |
| 61 | + (condition-case nil |
| 62 | + (backward-sexp) |
| 63 | + ;; "Containing expression ends prematurely" on an empty |
| 64 | + ;; parameter list. |
| 65 | + (scan-error (goto-char limit)))) |
| 66 | + (if found |
| 67 | + (setcdr function-data (cons (substring-no-properties (match-string 0)) |
| 68 | + (cdr function-data)))))) |
| 69 | + |
| 70 | +(defun mbedtls-test-data-parse-function-file-contents () |
| 71 | + (let ((entries nil) |
| 72 | + (case-fold-search nil)) |
| 73 | + (while (search-forward-regexp "^/\\*\\s-+BEGIN_CASE\\>" nil t) |
| 74 | + (forward-line) |
| 75 | + (when (looking-at "[^\n()]*\\s-\\([0-9A-Z_a-z]+\\)\\s-*(") |
| 76 | + (let ((function-data (list (substring-no-properties (match-string 1)))) |
| 77 | + (arguments-start (match-end 0))) |
| 78 | + (goto-char (1- (match-end 0))) |
| 79 | + (forward-sexp) |
| 80 | + (backward-char) |
| 81 | + (mbedtls-test-data-parse-function-argument-name function-data |
| 82 | + arguments-start) |
| 83 | + (when (cdr function-data) |
| 84 | + (while (search-backward "," arguments-start t) |
| 85 | + (mbedtls-test-data-parse-function-argument-name function-data |
| 86 | + arguments-start))) |
| 87 | + (setq entries (cons (list (car function-data) |
| 88 | + (apply #'vector (cdr function-data))) |
| 89 | + entries))))) |
| 90 | + entries)) |
| 91 | + |
| 92 | +(defun mbedtls-test-data-parse-function-file (&optional no-error) |
| 93 | + "Parse the .function file associated with the current .data file. |
| 94 | +Store the results in `mbedtls-test-data-functions'. |
| 95 | +If NO-ERROR is non-nil, do nothing if the .function file cannot be visited." |
| 96 | + (interactive "@") |
| 97 | + (or (buffer-live-p mbedtls-test-data-function-buffer) |
| 98 | + (setq mbedtls-test-data-function-buffer |
| 99 | + (mbedtls-test-data-visit-function-file nil no-error))) |
| 100 | + (when mbedtls-test-data-function-buffer |
| 101 | + (save-match-data |
| 102 | + (with-current-buffer mbedtls-test-data-function-buffer |
| 103 | + (save-excursion |
| 104 | + (save-restriction |
| 105 | + (widen) |
| 106 | + (goto-char (point-min)) |
| 107 | + (setq mbedtls-test-data-functions |
| 108 | + (mbedtls-test-data-parse-function-file-contents)))))))) |
| 109 | + |
| 110 | +(defun mbedtls-test-data-get-functions () |
| 111 | + "Return information about available test functions. |
| 112 | +This is the value of `mbedtls-test-data-functions' in the associated .function |
| 113 | +buffer. |
| 114 | +Use a cached result if available. Call `mbedtls-test-data-parse-function-file' |
| 115 | +to update the cache." |
| 116 | + (or (buffer-live-p mbedtls-test-data-function-buffer) |
| 117 | + (setq mbedtls-test-data-function-buffer |
| 118 | + (mbedtls-test-data-visit-function-file nil t))) |
| 119 | + (when mbedtls-test-data-function-buffer |
| 120 | + (with-current-buffer mbedtls-test-data-function-buffer |
| 121 | + (or mbedtls-test-data-functions |
| 122 | + (mbedtls-test-data-parse-function-file t)) |
| 123 | + mbedtls-test-data-functions))) |
| 124 | + |
| 125 | +(defun mbedtls-test-data-backward-argument (&optional arg) |
| 126 | + "Move backward to after the previous `:'. |
| 127 | +With a prefix argument, repeat that many times. If the prefix argument is |
| 128 | +negative, call `mbedtls-test-data-forward-argument' to move backward." |
| 129 | + (interactive "@p") |
| 130 | + (if (< arg 0) |
| 131 | + (mbedtls-test-data-forward-argument (- n)) |
| 132 | + (while (> arg 0) |
| 133 | + (backward-char) |
| 134 | + (skip-chars-backward "^:\n") |
| 135 | + (setq arg (1- arg))))) |
| 136 | + |
| 137 | +(defun mbedtls-test-data-forward-argument (&optional arg) |
| 138 | + "Move forward to after the next `:'. |
| 139 | +With a prefix argument, repeat that many times. If the prefix argument is |
| 140 | +negative, call `mbedtls-test-data-backward-argument' to move backward." |
| 141 | + (interactive "@p") |
| 142 | + (if (< arg 0) |
| 143 | + (mbedtls-test-data-backward-argument (- n)) |
| 144 | + (while (> arg 0) |
| 145 | + (skip-chars-forward "^:\n") |
| 146 | + (forward-char) |
| 147 | + (setq arg (1- arg))))) |
| 148 | + |
| 149 | +(defun mbedtls-test-data-goto-function-line () |
| 150 | + "Move point to the start of the line containing the function and its arguments." |
| 151 | + (interactive "@") |
| 152 | + (forward-paragraph) |
| 153 | + (if (bolp) |
| 154 | + (forward-line -1) |
| 155 | + (beginning-of-line))) |
| 156 | + |
| 157 | +(defun mbedtls-test-data-get-function-name-at-point () |
| 158 | + "Return the name of the test function in the test case containing point." |
| 159 | + (save-excursion |
| 160 | + (mbedtls-test-data-goto-function-line) |
| 161 | + (let ((beg (point))) |
| 162 | + (skip-chars-forward "^:\n") |
| 163 | + (buffer-substring-no-properties beg (point))))) |
| 164 | + |
| 165 | +(defun mbedtls-test-data-get-function-information-at-point () |
| 166 | + "Return information about the test function in the test case containing point. |
| 167 | +This is the relevant element from `mbedtls-test-data-functions'." |
| 168 | + (let ((info (mbedtls-test-data-get-functions))) |
| 169 | + (and info |
| 170 | + (assoc (mbedtls-test-data-get-function-name-at-point) info)))) |
| 171 | + |
| 172 | +(defun mbedtls-test-data-show-function-information (info arg) |
| 173 | + (let ((argument-names (cadr info))) |
| 174 | + (cond |
| 175 | + ((or (null arg) (<= arg 0)) |
| 176 | + (message "%s %s" |
| 177 | + (car info) |
| 178 | + (mapconcat #'identity argument-names " "))) |
| 179 | + ((> arg (length argument-names)) |
| 180 | + (message "%s takes %d arguments (no argument %d)" |
| 181 | + (car info) (length argument-names) arg)) |
| 182 | + (t |
| 183 | + (message "%d: %s" arg (aref argument-names (1- arg))))))) |
| 184 | + |
| 185 | +(defun mbedtls-test-data-show-argument-information (&optional arg) |
| 186 | + "Show the name of the argument around point. |
| 187 | +With a prefix argument, or if point is not on a function argument, show |
| 188 | +information about the function called by this test case." |
| 189 | + (interactive "@P") |
| 190 | + (let ((original-point (point)) |
| 191 | + (all-info (mbedtls-test-data-get-functions))) |
| 192 | + (when all-info |
| 193 | + (save-excursion |
| 194 | + (mbedtls-test-data-goto-function-line) |
| 195 | + (let* ((beg (point)) |
| 196 | + (function-name (progn |
| 197 | + (skip-chars-forward "^:\n") |
| 198 | + (buffer-substring-no-properties beg (point)))) |
| 199 | + (function-info (assoc function-name all-info)) |
| 200 | + (pos (if (> original-point (point)) |
| 201 | + (cl-count ?: (buffer-substring-no-properties |
| 202 | + beg original-point)) |
| 203 | + 0))) |
| 204 | + (mbedtls-test-data-show-function-information function-info pos)))))) |
| 205 | + |
| 206 | +(defun mbedtls-test-data-visit-function-definition (&optional arg) |
| 207 | + "Visit the function definition for the current test case. |
| 208 | +
|
| 209 | +With just \\[universal-argument] or a numerical prefix argument between 1 and 4, |
| 210 | +visit in another window with `switch-to-buffer-other-window'. With two |
| 211 | +\\[universal-argument] or a numerical prefix argument larger than 4, visit |
| 212 | +in another frame with `switch-to-buffer-other-frame'. With a negative prefix |
| 213 | +argument, don't display the function definition, only load the file and set |
| 214 | +point in the buffer visiting it. In any case, return the function file buffer." |
| 215 | + (interactive "@p") |
| 216 | + (let ((function-name (mbedtls-test-data-get-function-name-at-point))) |
| 217 | + (if function-name |
| 218 | + (let ((buffer (mbedtls-test-data-visit-function-file)) |
| 219 | + (switch-function (cond |
| 220 | + ((null arg) 'switch-to-buffer) |
| 221 | + ((symbolp arg) arg) |
| 222 | + ((and (integerp arg) (> arg 4)) |
| 223 | + 'switch-to-buffer-other-frame) |
| 224 | + ((and (integerp arg) (> arg 1)) |
| 225 | + 'switch-to-buffer-other-window) |
| 226 | + ((and (integerp arg) (< arg 0)) 'ignore) |
| 227 | + (t 'switch-to-buffer)))) |
| 228 | + (with-current-buffer buffer |
| 229 | + (funcall switch-function buffer) |
| 230 | + (push-mark) |
| 231 | + (goto-char (point-min)) |
| 232 | + (search-forward-regexp "^ */\\*+ *BEGIN_CASE\\b") |
| 233 | + (save-match-data |
| 234 | + (search-forward-regexp (concat "^\\w[^\n()]*\\s-" |
| 235 | + function-name |
| 236 | + "\\s-*(")) |
| 237 | + (backward-char) |
| 238 | + (forward-sexp) |
| 239 | + (forward-line)) |
| 240 | + buffer)) |
| 241 | + (message "Unable to determine the test case function name")))) |
| 242 | + |
| 243 | +(defun mbedtls-test-data-copy-to-top () |
| 244 | + "Copy the current test case to the top of the file." |
| 245 | + (interactive "@*") |
| 246 | + (save-excursion |
| 247 | + (save-restriction |
| 248 | + (let* ((begin (progn (backward-paragraph) |
| 249 | + (skip-chars-forward "\n") |
| 250 | + (point))) |
| 251 | + (end (progn (forward-paragraph) |
| 252 | + (point))) |
| 253 | + (text (buffer-substring-no-properties begin end))) |
| 254 | + (goto-char (point-min)) |
| 255 | + (insert text "\n#### ^^^^ Temporary copy ^^^^ ####\n\n") |
| 256 | + (if (interactive-p) |
| 257 | + (message "Copied %s" |
| 258 | + (save-match-data |
| 259 | + (substring text 0 (string-match "\n" text))))))))) |
| 260 | + |
| 261 | +(defvar mbedtls-test-data-mode-font-lock-keywords |
| 262 | + '( |
| 263 | + ("^#.*$" (0 font-lock-comment-face)) |
| 264 | + ("^depends_on:" |
| 265 | + (0 font-lock-keyword-face) |
| 266 | + ("[^\n:]+" () () |
| 267 | + (0 font-lock-builtin-face))) |
| 268 | + ("^\\([A-Z_a-z][0-9A-Z_a-z]*\\)\\(:\\)" |
| 269 | + (1 font-lock-function-name-face) |
| 270 | + (2 font-lock-keyword-face)) |
| 271 | + (":" (0 font-lock-keyword-face)) |
| 272 | + (".\\{66\\}\\(.+\\)" |
| 273 | + (1 font-lock-warning-face)) |
| 274 | + ) |
| 275 | + "Value of `font-lock-keywords' in Mbed TLS test data mode.") |
| 276 | + |
| 277 | +(defvar mbedtls-test-data-mode-font-lock-defaults |
| 278 | + '(mbedtls-test-data-mode-font-lock-keywords |
| 279 | + t |
| 280 | + nil |
| 281 | + ((?_ . "w"))) |
| 282 | + "Value of `font-lock-defaults' in Mbed TLS test data mode.") |
| 283 | + |
| 284 | +(defvar mbedtls-test-data-mode-map |
| 285 | + (let ((map (make-sparse-keymap))) |
| 286 | + (substitute-key-definition 'backward-sentence |
| 287 | + 'mbedtls-test-data-backward-argument |
| 288 | + map global-map) |
| 289 | + (substitute-key-definition 'forward-sentence |
| 290 | + 'mbedtls-test-data-forward-argument |
| 291 | + map global-map) |
| 292 | + (define-key map "\C-c\C-a" 'mbedtls-test-data-show-argument-information) |
| 293 | + (define-key map "\C-c\C-f" 'mbedtls-test-data-visit-function-definition) |
| 294 | + (define-key map "\C-c\C-n" 'mbedtls-test-data-parse-function-file) |
| 295 | + (define-key map "\C-c\C-t" 'mbedtls-test-data-copy-to-top) |
| 296 | + map) |
| 297 | + "Keymap used in Mbed TLS test data mode.") |
| 298 | + |
| 299 | +(defvar mbedtls-test-data-mode-hook nil |
| 300 | + "Normal hook to run when entering Mbed TLS test data mode.") |
| 301 | + |
| 302 | +(defun mbedtls-test-data-mode () |
| 303 | + "Major mode to edit Mbed TLS test data files. |
| 304 | +
|
| 305 | +\\{mbedtls-test-data-mode-map}" |
| 306 | + (interactive) |
| 307 | + (kill-all-local-variables) |
| 308 | + (setq major-mode 'mbedtls-test-data-mode) |
| 309 | + (setq mode-name "MbedTLS") |
| 310 | + (set-syntax-table mbedtls-test-data-mode-syntax-table) |
| 311 | + (setq comment-start "# " |
| 312 | + comment-end "") |
| 313 | + (use-local-map mbedtls-test-data-mode-map) |
| 314 | + (make-variable-buffer-local 'font-lock-defaults) |
| 315 | + (setq font-lock-defaults mbedtls-test-data-mode-font-lock-defaults) |
| 316 | + (mbedtls-test-data-parse-function-file t) |
| 317 | + (run-hooks 'mbedtls-test-data-mode-hook)) |
| 318 | + |
| 319 | +(provide 'mbedtls-test-data-mode) |
| 320 | + |
| 321 | +;;; That's all. |
0 commit comments