+\f
+
+;;; Completion.
+
+(defun makefile-complete ()
+ "Perform completion on Makefile construct preceding point.
+Can complete variable and target names.
+The context determines which are considered."
+ (interactive)
+ (let* ((beg (save-excursion
+ (skip-chars-backward "^$(){}:#= \t\n")
+ (point)))
+ (try (buffer-substring beg (point)))
+ (do-macros nil)
+ (paren nil))
+
+ (save-excursion
+ (goto-char beg)
+ (let ((pc (preceding-char)))
+ (cond
+ ;; Beginning of line means anything.
+ ((bolp)
+ ())
+
+ ;; Preceding "$" means macros only.
+ ((= pc ?$)
+ (setq do-macros t))
+
+ ;; Preceding "$(" or "${" means macros only.
+ ((and (or (= pc ?{)
+ (= pc ?\())
+ (progn
+ (setq paren pc)
+ (backward-char)
+ (and (not (bolp))
+ (= (preceding-char) ?$))))
+ (setq do-macros t)))))
+
+ ;; Try completion.
+ (let* ((table (append (if do-macros
+ '()
+ makefile-target-table)
+ makefile-macro-table))
+ (completion (try-completion try table)))
+ (cond
+ ;; Exact match, so insert closing paren or colon.
+ ((eq completion t)
+ (insert (if do-macros
+ (if (eq paren ?{)
+ ?}
+ ?\))
+ (if (save-excursion
+ (goto-char beg)
+ (bolp))
+ ":"
+ " "))))
+
+ ;; No match.
+ ((null completion)
+ (message "Can't find completion for \"%s\"" try)
+ (ding))
+
+ ;; Partial completion.
+ ((not (string= try completion))
+ ;; FIXME it would be nice to supply the closing paren if an
+ ;; exact, unambiguous match were found. That is not possible
+ ;; right now. Ditto closing ":" for targets.
+ (delete-region beg (point))
+
+ ;; DO-MACROS means doing macros only. If not that, then check
+ ;; to see if this completion is a macro. Special insertion
+ ;; must be done for macros.
+ (if (or do-macros
+ (assoc completion makefile-macro-table))
+ (let ((makefile-use-curly-braces-for-macros-p
+ (or (eq paren ?{)
+ makefile-use-curly-braces-for-macros-p)))
+ (delete-backward-char 2)
+ (makefile-do-macro-insertion completion)
+ (delete-backward-char 1))
+
+ ;; Just insert targets.
+ (insert completion)))
+
+ ;; Can't complete any more, so make completion list. FIXME
+ ;; this doesn't do the right thing when the completion is
+ ;; actually inserted. I don't think there is an easy way to do
+ ;; that.
+ (t
+ (message "Making completion list...")
+ (let ((list (all-completions try table)))
+ (with-output-to-temp-buffer "*Completions*"
+ (display-completion-list list)))
+ (message "Making completion list...done"))))))
+
+\f
+
+;; Backslashification. Stolen from cc-mode.el.
+
+(defun makefile-backslash-region (from to delete-flag)
+ "Insert, align, or delete end-of-line backslashes on the lines in the region.
+With no argument, inserts backslashes and aligns existing backslashes.
+With an argument, deletes the backslashes.
+
+This function does not modify the last line of the region if the region ends
+right at the start of the following line; it does not modify blank lines
+at the start of the region. So you can put the region around an entire macro
+definition and conveniently use this command."
+ (interactive "r\nP")
+ (save-excursion
+ (goto-char from)
+ (let ((column makefile-backslash-column)
+ (endmark (make-marker)))
+ (move-marker endmark to)
+ ;; Compute the smallest column number past the ends of all the lines.
+ (if makefile-backslash-align
+ (progn
+ (if (not delete-flag)
+ (while (< (point) to)
+ (end-of-line)
+ (if (= (preceding-char) ?\\)
+ (progn (forward-char -1)
+ (skip-chars-backward " \t")))
+ (setq column (max column (1+ (current-column))))
+ (forward-line 1)))
+ ;; Adjust upward to a tab column, if that doesn't push
+ ;; past the margin.
+ (if (> (% column tab-width) 0)
+ (let ((adjusted (* (/ (+ column tab-width -1) tab-width)
+ tab-width)))
+ (if (< adjusted (window-width))
+ (setq column adjusted))))))
+ ;; Don't modify blank lines at start of region.
+ (goto-char from)
+ (while (and (< (point) endmark) (eolp))
+ (forward-line 1))
+ ;; Add or remove backslashes on all the lines.
+ (while (and (< (point) endmark)
+ ;; Don't backslashify the last line
+ ;; if the region ends right at the start of the next line.
+ (save-excursion
+ (forward-line 1)
+ (< (point) endmark)))
+ (if (not delete-flag)
+ (makefile-append-backslash column)
+ (makefile-delete-backslash))
+ (forward-line 1))
+ (move-marker endmark nil))))
+
+(defun makefile-append-backslash (column)
+ (end-of-line)
+ ;; Note that "\\\\" is needed to get one backslash.
+ (if (= (preceding-char) ?\\)
+ (progn (forward-char -1)
+ (delete-horizontal-space)
+ (indent-to column (if makefile-backslash-align nil 1)))
+ (indent-to column (if makefile-backslash-align nil 1))
+ (insert "\\")))
+
+(defun makefile-delete-backslash ()
+ (end-of-line)
+ (or (bolp)
+ (progn
+ (forward-char -1)
+ (if (looking-at "\\\\")
+ (delete-region (1+ (point))
+ (progn (skip-chars-backward " \t") (point)))))))
+
+\f
+
+;; Filling
+
+(defun makefile-fill-paragraph (arg)
+ ;; Fill comments, backslashed lines, and variable definitions
+ ;; specially.
+ (save-excursion
+ (beginning-of-line)
+ (cond
+ ((looking-at "^#+ ")
+ ;; Found a comment. Set the fill prefix, and find the paragraph
+ ;; boundaries by searching for lines that look like comment-only
+ ;; lines.
+ (let ((fill-prefix (match-string-no-properties 0))
+ (fill-paragraph-function nil))
+ (save-excursion
+ (save-restriction
+ (narrow-to-region
+ ;; Search backwards.
+ (save-excursion
+ (while (and (zerop (forward-line -1))
+ (looking-at "^#")))
+ ;; We may have gone too far. Go forward again.
+ (or (looking-at "^#")
+ (forward-line 1))
+ (point))
+ ;; Search forwards.
+ (save-excursion
+ (while (looking-at "^#")
+ (forward-line))
+ (point)))
+ (fill-paragraph nil)
+ t))))
+
+ ;; Must look for backslashed-region before looking for variable
+ ;; assignment.
+ ((or (eq (char-before (line-end-position 1)) ?\\)
+ (eq (char-before (line-end-position 0)) ?\\))
+ ;; A backslash region. Find beginning and end, remove
+ ;; backslashes, fill, and then reapply backslahes.
+ (end-of-line)
+ (let ((beginning
+ (save-excursion
+ (end-of-line 0)
+ (while (= (preceding-char) ?\\)
+ (end-of-line 0))
+ (forward-char)
+ (point)))
+ (end
+ (save-excursion
+ (while (= (preceding-char) ?\\)
+ (end-of-line 2))
+ (point))))
+ (save-restriction
+ (narrow-to-region beginning end)
+ (makefile-backslash-region (point-min) (point-max) t)
+ (let ((fill-paragraph-function nil))
+ (fill-paragraph nil))
+ (makefile-backslash-region (point-min) (point-max) nil)
+ (goto-char (point-max))
+ (if (< (skip-chars-backward "\n") 0)
+ (delete-region (point) (point-max))))))
+
+ ((looking-at makefile-macroassign-regex)
+ ;; Have a macro assign. Fill just this line, and then backslash
+ ;; resulting region.
+ (save-restriction
+ (narrow-to-region (point) (line-beginning-position 2))
+ (let ((fill-paragraph-function nil))
+ (fill-paragraph nil))
+ (makefile-backslash-region (point-min) (point-max) nil)))))
+
+ ;; Always return non-nil so we don't fill anything else.
+ t)
+
+\f
+