purcell / package-lint

A linting library for elisp package metadata

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Honor prefix from package group i.e. defgroup definition

thierryvolpiatto opened this issue · comments

package-lint should look in prefix group to see if this prefix match symbols in package, see melpa/melpa#7383

Here a patch that fix this issue:

diff --git a/package-lint.el b/package-lint.el
index 9fbb119..86de1b6 100644
--- a/package-lint.el
+++ b/package-lint.el
@@ -875,19 +875,21 @@ Valid definition names are:
 - a NAME matching `package-lint--sane-prefixes', or
 
 - a NAME whose POSITION in the buffer denotes a global definition."
-  (or (string-prefix-p prefix name)
-      (string-match-p package-lint--sane-prefixes name)
-      (string-match-p (rx-to-string `(seq string-start (or "define" "defun" "defvar" "defface" "with") "-" ,prefix)) name)
-      (string-match-p (rx-to-string  `(seq string-start "global-" ,prefix (or "-mode" (seq "-" (* any) "-mode")) string-end)) name)
-      (let ((short-prefix (car (cl-remove-if-not (lambda (e) (string-prefix-p e prefix))
-                                             (mapcar #'car package-lint--allowed-prefix-mappings)))))
-        (when short-prefix
-          (package-lint--valid-prefix-mapping-p short-prefix prefix name)))
-      (when position
-        (goto-char position)
-        (looking-at-p (rx (*? space) "(" (*? space)
-                          (or "defadvice" "cl-defmethod")
-                          symbol-end)))))
+  (let ((prefix-group (get (intern-soft prefix) 'custom-prefix)))
+    (or (string-prefix-p prefix name)
+        (and prefix-group (string-prefix-p prefix-group name))
+        (string-match-p package-lint--sane-prefixes name)
+        (string-match-p (rx-to-string `(seq string-start (or "define" "defun" "defvar" "defface" "with") "-" ,prefix)) name)
+        (string-match-p (rx-to-string  `(seq string-start "global-" ,prefix (or "-mode" (seq "-" (* any) "-mode")) string-end)) name)
+        (let ((short-prefix (car (cl-remove-if-not (lambda (e) (string-prefix-p e prefix))
+                                                   (mapcar #'car package-lint--allowed-prefix-mappings)))))
+          (when short-prefix
+            (package-lint--valid-prefix-mapping-p short-prefix prefix name)))
+        (when position
+          (goto-char position)
+          (looking-at-p (rx (*? space) "(" (*? space)
+                            (or "defadvice" "cl-defmethod")
+                            symbol-end))))))
 
 (defun package-lint--check-defs-prefix (prefix definitions)
   "Verify that symbol DEFINITIONS start with package PREFIX."

It is technically possible as you have shown, but we need to use limited resources under the common rule on Elisp not having namespaces.
Currently, I consider it intentional that package-lint does not interpret the prefix of defgroup.

From the user's convention, if they install isearch-light from package.el, the user will think of isearch-light as a prefix and look for some interactive functions. It is not intuitive to start with isl.
I believe package-lint contributes to a predictable and easy-to-use Emacs ecosystem by enforcing this convention on package authors as a criterion for registration to MELPA.

Ok, thanks for your answer.

Yes, that's right: here we'd insist on the symbol prefix matching the package name for MELPA submissions. This might seem overly draconian, but it leads to enhanced consistency in the ecosystem and avoids unexpected clashes of the same abbreviated prefixes across different packages.

Yes, that's right: here we'd insist on the symbol prefix matching the package name for MELPA submissions. This might seem overly draconian, but it leads to enhanced consistency in the ecosystem and avoids unexpected clashes of the same abbreviated prefixes across different packages.

I am sorry for commenting on a closed issue. I was just looking around to see why package-lint does not recognize short internal prefix and found this discussion.

It is by now a common practice to use double dashed prefixes for internal functions. Internal functions should rather not be used by users. I personally tend to use the package name as a long prefix for the user visible functions, exactly for the reasons mentioned here, but a shortened double dashed prefix, usually an acronym on the package name, for internal functions that users should not use. I think it would be preferable if package-lint could honour double dashed prefixes found in defgroup.

Just my personal thought, I am thinking of proposing a package for Melpa, and was just checkdoc-ed it and linte-ed, and found that my functions are not prefixed correctly :).

I can't really add more to what I wrote before: the risk of clashes is much higher if authors use short acronymic internal prefixes, plus it's harder to see where symbols came from. There's no fundamental difference in this regard between public and internal symbols, so why not just namespace everything consistently? The only counter-argument is convenience for the original author, which isn't a strong enough argument in our view.

the risk of clashes is much higher if authors use short acronymic internal prefixes

Sure, definitely agree that risk is higher, and while I do use acronyms in my elisp, I don't say everyone should use acronyms. Actually, I completely agree it is a bad practice. But it is fully possible to find a shorter prefix, and certainly to use at different prefix for internal (private) symbols than to prefix them with package name.

There's no fundamental difference in this regard between public and internal symbols, so why not just namespace everything consistently?

Yes, there is; the difference is that those symbols should not be used by 3rd party users. They are often internal for a reason; a subject of change due to development being in progress, or because they don't make sense to use in context outside some other functionality.

Such symbols also populate the global namespace, so when we complete in M-: or some other places, they come up as unnecessary clutter. The only time when others should see them is if they debug the package, but someone who debugs a package probably does not have a problem to see where a symbol comes from neither (visible in stack trace, step through with edebug or c-h v/f).

I don't know the particular reason(s) why they have implemented the possibility to have any number of prefixes in Emacs package, but I think the above one is a valid reason to have internal symbols named with different prefix than "public" symbols. Maybe package lint can check if a prefix is too short, or something similar, but I think it would be good to honour package declared prefixes.

The only counter-argument is convenience for the original author, which isn't a strong enough argument in our view.

Yes, I can definitely agree that only original author's convenience is not good enough reason, but having different prefix is not so much original developer's convenience, but 3rd party devs when completing, looking up docs, etc.

Yes, there is; the difference is that those symbols should not be used by 3rd party users. They are often internal for a reason; a subject of change due to development being in progress, or because they don't make sense to use in context outside some other functionality.

I'm not arguing the utility of private identifiers: just that the prefixes should be package- and package-- for public and private symbols respectively.

Re. your completion point, you're arguing that the symbols should go into different namespaces for the sake of more convenient completion in some cases, and I'd say that completion backends can/should easily be made to deal with that, e.g. by sorting foo-bar ahead of foo--bar-baz. Totally agree that completion gets annoying in some of these cases. In the end, all of this is a mess of compromises due to lack of first-class namespaces.

Sorry, for bit late answer, didn't realized it is already 6 days ago :-(.

I'd say that completion backends can/should easily be made to deal with that

Yes, I can agree with that one, they could even make double-dashed prefixes hidden in completions by default or choice. Hmm hmm, if herr Volpiatto is still reading here :-).

all of this is a mess of compromises due to lack of first-class namespaces.

Indeed, but we are regressing.

I still have though wish for package-lint to respect other prefixes declared in the package group :-). I am not trying to be annoying, and am certainly not about endless arguing, but my problem is that names are getting long. I like self-documenting code, so if you look at my original tests, most of my variables and functions have some describing names. Code becomes almost like prose (not mine, I am too bad for that yet, unfortunately), or at least like a formal doc. The issue with package names is that names become rather long, and I am mean to the level where they actually impact readability. For example, something like this:

(defvar org-leading-stars-re "^[ \t]*\\*+"
  "Regex used to recognize leading stars in org-headings.")

(defun org-hide-leading-stars--in-heading-p ()
  (save-excursion
    (goto-char (line-beginning-position))
    (looking-at-p org-hide-leading-stars-re))) 

If I am going to use the predicate 'org-hide-leading-stars--in-heding-p' somewhere in some function, that already is indented somewhat to the right, maybe in an 'or' or 'and' form, I am getting easily out of 80-column width. The name itself is 36 chars long, that is almost half of the column budget. The solution is to completely ditch the descriptive package name and choose something like "deathstars" or "starless" or whatever short which risks to be completely missed by someone looking around in packages buffer, or to end up with a not so descriptive private part of the name. Perhaps you have a suggestion, I am all ears, but I think the (secondary) private prefix is one way to solve the problem.

all of this is a mess of compromises due to lack of first-class namespaces.

Indeed, but we are regressing here, and this comment is so long already. I'll just mention, there are several namespace implementations for Emacs, that unfortunately no-one uses, me included :-(

No, I disagree, programmers should be aware of all symbols.

Be aware of, and looking at all time at, is not the same for me. I could imagine having option to type C- to toggle display of private symbols, but that is my preference.