(defun company-blueprint-backend (command &optional arg &rest ignored) (cl-case command (interactive (company-begin-backend 'company-blueprint-backend)) ; (prefix (and (bound-and-true-p dojo-minor-mode) ; This line would test for dojo-minor-mode (prefix (and (eq major-mode 'blueprint-mode) (blueprint-get-current-completion-prefix))) (candidates (blueprint-get-completion-candidates arg)) (annotation (blueprint-get-completion-annotation arg)) (meta (blueprint-get-completion-doc arg)) ) ) (defun blueprint-get-current-completion-prefix () ; < > " space (let* ((prev-open-pos (blueprint-get-prefix-start "<")) (prev-close-pos (blueprint-get-prefix-start ">")) (prev-quotation-pos (blueprint-get-prefix-start "\"")) (prev-eq-pos (blueprint-get-prefix-start "=")) (prev-comment-start-pos (blueprint-get-prefix-start "")) (prev-space-pos (blueprint-get-prefix-start "[[:space:]]")) (next-comment-end-pos (save-excursion (search-forward "-->" nil t))) (max-delimiter-pos (dojo-common-math-nil-aware-max prev-open-pos prev-close-pos prev-quotation-pos prev-eq-pos prev-space-pos))) (log-blueprint-completion (format "open-pos [%s], close-pos [%s], comment-start [%s], comment-end [%s], space [%s], next-comment-end [%s]" prev-open-pos prev-close-pos prev-comment-start-pos prev-comment-end-pos prev-space-pos next-comment-end-pos)) (cond ((and (not (null prev-comment-start-pos)) (<= prev-comment-start-pos (point)) ; Previous comment start before point (or (null prev-comment-end-pos) (< prev-comment-end-pos prev-comment-start-pos)) ; Previous comment end not after previous comment start (not (null next-comment-end-pos)) (>= next-comment-end-pos (point))) ; Next comment end after point (log-blueprint-completion (format "Inside comment, will not return any prefix.")) nil) ((null max-delimiter-pos) (log-blueprint-completion (format "No useful delimiter before point.")) nil) ((eq max-delimiter-pos prev-open-pos) (log-blueprint-completion (format "After tag start, will return [%s]" (buffer-substring prev-open-pos (point)))) (buffer-substring (1- prev-open-pos) (point))) ((eq max-delimiter-pos prev-space-pos) (log-blueprint-completion (format "After space, will return [%s]" (buffer-substring prev-space-pos (point)))) (buffer-substring prev-space-pos (point))) ((eq max-delimiter-pos prev-eq-pos) (log-blueprint-completion (format "After equal sign, will return [%s]" (buffer-substring prev-eq-pos (point)))) (buffer-substring prev-eq-pos (point))) ((eq max-delimiter-pos prev-quotation-pos) (save-excursion (let ((old-point (point))) (goto-char (1- prev-quotation-pos)) (let* ((prev-prev-quotation-pos (blueprint-get-prefix-start "\"")) (prev-prev-eq-pos (blueprint-get-prefix-start "=")) (prev-max-delimiter-pos (dojo-common-math-nil-aware-max prev-prev-quotation-pos prev-prev-eq-pos))) (log-blueprint-completion (format "prev-prev-quotation-pos [%s], prev-prev-eq-pos [%s], prev-max-delimiter-pos [%s]" prev-prev-quotation-pos prev-prev-eq-pos prev-max-delimiter-pos)) (cond ((or (null prev-max-delimiter-pos) (eq prev-prev-eq-pos prev-max-delimiter-pos)) (log-blueprint-completion (format "After quotation, will return [%s]" (buffer-substring (1- prev-quotation-pos) old-point))) (buffer-substring (1- prev-quotation-pos) old-point)) (t (log-blueprint-completion (format "Probably after closing quotation, will return nil.")) nil)))))) (t (log-blueprint-completion (format "None of the above cases matched.")) nil)))) (defun blueprint-get-prefix-start (regexp) (save-excursion (let ((result (re-search-backward regexp nil t))) (if result (1+ result) nil)))) (defun blueprint-get-token (regexp) (save-excursion (let ((result (re-search-forward regexp nil t))) (if result result nil)))) (defun blueprint-get-token-backwards (regexp) (save-excursion (re-search-backward regexp nil t))) (defun blueprint-get-completion-candidates (prefix) ; (log-blueprint-completion (format "Called get-completion-candidates with prefix [%s]" prefix)) (let* ((ancestor-path (blueprint-get-ancestor-path)) (type (nth (1- (length ancestor-path)) ancestor-path)) (candidates (cond ((string= type "---") ; (log-blueprint-completion (format "Outside node name / attribute name / attribute value, will return nothing.")) ()) ((string= type "node-name") (blueprint-get-node-name-candidates prefix ancestor-path)) ((string= type "attribute-name") (blueprint-get-attribute-name-candidates prefix ancestor-path)) ((string= type "attribute-value") (blueprint-get-attribute-value-candidates prefix ancestor-path)) (t (log-blueprint-completion (format "Unknown completion case [%s]" type)) ())))) candidates)) (defun blueprint-get-candidates-if-prefix (prefix candidates) (let ((completions ())) (dolist (candidate candidates) (if (string-prefix-p prefix candidate) (progn (push candidate completions)))) completions)) (defun blueprint-get-ancestor-path () (save-excursion (let* ((orig-point (point)) (prev-point (point)) (within-comment nil) (last-comment-close-pos (blueprint-get-token-backwards "-->")) (last-empty-tag-close-pos (blueprint-get-token-backwards "*/>")) (last-tag-close-pos (blueprint-get-token-backwards "[^-][^-/]>")) (last-tag-open-pos (blueprint-get-token-backwards "<[^/]")) (last-empty-tag-open-pos (blueprint-get-token-backwards "= level 0) (null abort-malformed)) ; (log-blueprint-completion (format "Positions: [%s], --> [%s], /> [%s], > [%s], < [%s], ")) (setq last-empty-tag-close-pos (blueprint-get-token-backwards "*/>")) (setq last-tag-close-pos (blueprint-get-token-backwards "[^-][^-/]>")) (setq last-tag-open-pos (blueprint-get-token-backwards "<[^/]")) (setq last-empty-tag-open-pos (blueprint-get-token-backwards "', as this might lead to a wrong ancestor-path; the code below ; might erroneously assume that we are *inside* the tag which ends there. (if (and (> (point) 0) (not (eq (char-after (1- (point))) ?\>))) (backward-char)) (log-blueprint-completion (format "Starting backward-up loop at position [%s]" (point))) (while (and (< (point-min) (point)) ;; Doesn't error if point is at beginning of buffer (condition-case nil (progn (nxml-backward-up-element) ; always returns nil t) (error nil))) (let* ((prefix (xmltok-start-tag-prefix)) (local-name (xmltok-start-tag-local-name)) (qualified-name (concat prefix ":" local-name))) (setq path (cons (blueprint-get-start-tag-qualified-name) path)))) ; (log-blueprint-completion (format "Returning ancestor-path [%s]" path)) path)))) (defun blueprint-get-start-tag-qualified-name () (let* ((prefix (xmltok-start-tag-prefix)) (local-name (xmltok-start-tag-local-name)) (qualified-name (concat prefix ":" local-name))) qualified-name)) (defun blueprint-get-node-name-candidates (prefix ancestor-path) (log-blueprint-completion (format "Retrieving node-name candidates for prefix [%s]" prefix)) (log-blueprint-completion (format "... ancestor-path is [%s]" ancestor-path)) (let* ((parent-node (nth (- (length ancestor-path) 2) ancestor-path)) (starts-with-bracket (starts-with prefix "<")) (colon-pos (string-match ":" prefix)) ; Unify prefix to the form '