X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/8f2c03248b5517d82588a05f357805cbb0f384ed..9b89556880e326ec3391cfda2a87eada101eaaae:/company-files.el diff --git a/company-files.el b/company-files.el index a450526a0..c19d3d6cb 100644 --- a/company-files.el +++ b/company-files.el @@ -1,6 +1,6 @@ -;;; company-files.el --- company-mode completion back-end for file names +;;; company-files.el --- company-mode completion backend for file paths -;; Copyright (C) 2009-2011, 2013 Free Software Foundation, Inc. +;; Copyright (C) 2009-2011, 2014-2015 Free Software Foundation, Inc. ;; Author: Nikolaj Schumacher @@ -28,62 +28,93 @@ (require 'company) (require 'cl-lib) -(defun company-files-directory-files (dir prefix) +(defun company-files--directory-files (dir prefix) (ignore-errors - (if (equal prefix "") - (directory-files dir nil "\\`[^.]\\|\\`.[^.]") - (file-name-all-completions prefix dir)))) - -(defvar company-files-regexps - (let ((begin (if (eq system-type 'windows-nt) - "[a-z][A-Z]\\" - "~?/"))) + ;; Don't use directory-files. It produces directories without trailing /. + (let ((comp (sort (file-name-all-completions prefix dir) + (lambda (s1 s2) (string-lessp (downcase s1) (downcase s2)))))) + (if (equal prefix "") + (delete "../" (delete "./" comp)) + comp)))) + +(defvar company-files--regexps + (let* ((root (if (eq system-type 'windows-nt) + "[a-zA-Z]:/" + "/")) + (begin (concat "\\(?:\\.\\{1,2\\}/\\|~/\\|" root "\\)"))) (list (concat "\"\\(" begin "[^\"\n]*\\)") (concat "\'\\(" begin "[^\'\n]*\\)") (concat "\\(?:[ \t]\\|^\\)\\(" begin "[^ \t\n]*\\)")))) -(defun company-files-grab-existing-name () - ;; Grab file names with spaces, only when they include quotes. +(defun company-files--grab-existing-name () + ;; Grab the file name. + ;; When surrounded with quotes, it can include spaces. (let (file dir) - (and (dolist (regexp company-files-regexps) + (and (cl-dolist (regexp company-files--regexps) (when (setq file (company-grab-line regexp 1)) (cl-return file))) + (company-files--connected-p file) (setq dir (file-name-directory file)) (not (string-match "//" dir)) (file-exists-p dir) - (file-name-all-completions (file-name-nondirectory file) dir) file))) -(defvar company-files-completion-cache nil) +(defun company-files--connected-p (file) + (or (not (file-remote-p file)) + (file-remote-p file nil t))) -(defun company-files-complete (prefix) +(defun company-files--trailing-slash-p (file) + ;; `file-directory-p' is very expensive on remotes. We are relying on + ;; `file-name-all-completions' returning directories with trailing / instead. + (let ((len (length file))) + (and (> len 0) (eq (aref file (1- len)) ?/)))) + +(defvar company-files--completion-cache nil) + +(defun company-files--complete (prefix) (let* ((dir (file-name-directory prefix)) (file (file-name-nondirectory prefix)) - candidates) - (unless (equal dir (car company-files-completion-cache)) - (dolist (file (company-files-directory-files dir file)) - (setq file (concat dir file)) - (push file candidates) - (when (file-directory-p file) - ;; Add one level of children. - (dolist (child (company-files-directory-files file "")) - (push (concat file - (unless (eq (aref file (1- (length file))) ?/) "/") - child) candidates)))) - (setq company-files-completion-cache (cons dir (nreverse candidates)))) + (key (list file + (expand-file-name dir) + (nth 5 (file-attributes dir)))) + (completion-ignore-case read-file-name-completion-ignore-case)) + (unless (company-file--keys-match-p key (car company-files--completion-cache)) + (let* ((candidates (mapcar (lambda (f) (concat dir f)) + (company-files--directory-files dir file))) + (directories (unless (file-remote-p dir) + (cl-remove-if-not (lambda (f) + (and (company-files--trailing-slash-p f) + (not (file-remote-p f)) + (company-files--connected-p f))) + candidates))) + (children (and directories + (cl-mapcan (lambda (d) + (mapcar (lambda (c) (concat d c)) + (company-files--directory-files d ""))) + directories)))) + (setq company-files--completion-cache + (cons key (append candidates children))))) (all-completions prefix - (cdr company-files-completion-cache)))) + (cdr company-files--completion-cache)))) + +(defun company-file--keys-match-p (new old) + (and (equal (cdr old) (cdr new)) + (string-prefix-p (car old) (car new)))) ;;;###autoload (defun company-files (command &optional arg &rest ignored) - "`company-mode' completion back-end existing file names." + "`company-mode' completion backend existing file names. +Completions works for proper absolute and relative files paths. +File paths with spaces are only supported inside strings." (interactive (list 'interactive)) (cl-case command (interactive (company-begin-backend 'company-files)) - (prefix (company-files-grab-existing-name)) - (candidates (company-files-complete arg)) + (prefix (company-files--grab-existing-name)) + (candidates (company-files--complete arg)) (location (cons (dired-noselect (file-name-directory (directory-file-name arg))) 1)) + (post-completion (when (company-files--trailing-slash-p arg) + (delete-char -1))) (sorted t) (no-cache t)))