Skip to content

Conversation

@Konfekt
Copy link
Contributor

@Konfekt Konfekt commented Mar 18, 2025

this lets Vim look for keywords in files included by import

@zzzyxwvut
Copy link
Owner

As you already know, Java sources are usually distributed as
Jar files. I think adequate support for traversing import
declarations is already provided with &includeexpr, please
see :help ft-java-plugin.

I couldn't say much about &define's utility, but one thing
I noticed is that typing [i on String may find matches in
comments. Unfortunately, [d and [D try hard for a good
while on String in String.javaand report E388. Why
not (re-)generate tags every now and then and use Ctrl-]
instead?

@Konfekt
Copy link
Contributor Author

Konfekt commented Mar 19, 2025

&define is Vim's built-in way. Of course one prefers to set up rather an LSP and Ctags as a fallback, but it's better to have something working out of the box as well, that's what &define was meant for.

I think adequate support for traversing import
declarations is already provided with &includeexpr, please

If I import a file, then :ilist doesn't give any matches of the imported file, which is what &include is meant for, see vim/vim#16916 (comment)

However, once setlocal include=^\\s*import\\s set, it does.

see :help ft-java-plugin.

I don't see much about &includeexpr over there. Making gf work for files in zip archives is quite a feat, thank you, but making gf work on imports is only a side benefit of &includeexpr.

@zzzyxwvut
Copy link
Owner

:help include-search paints quite a picture:

When a line is encountered that includes another file, that file is searched
before continuing in the current buffer. Files included by included files are
also searched. When an include file could not be found it is silently
ignored. Use the |:checkpath| command to discover which files could not be
found, possibly your 'path' option is not set up correctly. Note: the
included file is searched, not a buffer that may be editing that file. Only
for the current file the lines in the buffer are used.

The more imports a source file has the more appealing tags
are as an alternative.

If searches with [i and [I are enabled for Java sources, it
is essential that [d and [D are also set out to behave as
intended, for the most part. The current &define patterns
have issues that will lead up to E388s; compare them with
declaration production rules for classes and their methods
and interfaces and their methods. Remember that non-ASCII
identifiers have been supported in Java all along. It is
unclear from &define examples why #define declarations
(and Java type declarations) need not trailing identifier
patterns but C++ const type declarations (and Java method
declarations) do need it. Grouping is better be done with
\\%(...\\\|...\\) (note: \\\|).

With import module foo; in the works (see JEP 494), I fear
it would take a dedicated plugin and a volunteer to write it
to figure out all the ins and outs needed for &includeexpr.

@Konfekt
Copy link
Contributor Author

Konfekt commented Mar 20, 2025

the current &define patterns have issues

It's far from perfect and likely perfection with a pattern is unachievable; in practice it'll mostly work, in theory regexes only get you so far

non-ASCII identifiers have been supported in Java all along

There's [[:upper:][:lower:]] to match those and I now used them where possible. However, &isident is global, where it should be local. So for the identifier the characters defined by \i will have to do (though in practice function names with diacritics, emoticons, ... are rarely encountered in professional environments).

import module foo; in the works

I added matching for these.

@zzzyxwvut
Copy link
Owner

I have tried using the proposed settings (at 1d7bcc) for
&define and &include with vanilla Vim 7.0 and v9.1.1232 in
a few published Java syntax test files:

cd runtime/syntax/testdir/input

vim -u NONE --cmd 'set rtp^=~/.vim' \
  --cmd 'filetype plugin on | syntax on | set hlsearch' \
  java_comments_html.java \
  java_contextual_keywords.java \
  java_generics.java \
  java_methods_style.java

As is, &l:define doesn't aid in finding local declarations
with [d unless its |s are escaped thus \\\|:

setlocal define?

" Try typing "[d" on a name of a local method or a local type.

let @/ = &l:define

" E486
normal n

let &l:define = escape(&l:define, '|')
let @/ = &l:define
normal n

There are missing type modifiers to match, (non-)sealed, and
modifier repetition is broken, e.g. it is not possible to
discover public static native void foo();; generic method
declarations cannot be found and so are method declarations
with elementless annotation modifiers, @Override void foo()
(see examples in Java syntax tests).

Given that Java compilers don't care about name styles used,
i.e. whether an identifier starts with a small or a capital
letter, neither should we: \\<\\%(\\K\\k*\\.\\)*\\K\\k*\\>.

As I mention the subject elsewhere, supporting import module
is a complex task, worthy of a dedicated plugin; but we may
consider import static foo.staticMethod; declarations.

@Konfekt
Copy link
Contributor Author

Konfekt commented Mar 26, 2025

Given that Java compilers don't care about name styles used,
i.e. whether an identifier starts with a small or a capital
letter

modifier repetition is broken, e.g. it is not possible to
discover public static native void foo();;

As is, &l:define doesn't aid in finding local declarations
with [d unless its |s are escaped thus \|

There are missing type modifiers to match, (non-)sealed,

elementless annotation modifiers

I hope to have addressed these each in a separate commit.

generic method declarations cannot be found

Maybe you could a hint on what is meant and how to fix &define to this end?

@Konfekt
Copy link
Contributor Author

Konfekt commented Mar 26, 2025

we may consider import static foo.staticMethod; declarations.

I added detection of these as well

@zzzyxwvut
Copy link
Owner

I second that :lets are less noisy than :setlocals with
respect to \\\| when used for &l:define and &l:include;
let's use :lets for both options. Since all patterns in
distributed Java configuration files comply with the syntax
of \ms, sprinkling \vs here and there will jar with the
current regexp style; so no \vs, please. Both [:keyword:]
(v8.1.0862) and .. (v8.1.1114) expressions are incompatible
with Vim 7.0, the base supported version:

"""" STRIVE TO REMAIN COMPATIBLE FOR AT LEAST VIM 7.0.

Patterns can be improved upon. I see more comprehensive
results matching type and method declarations in Java syntax
test files of this repository after applying the next patch
to master (at ae0f451), followed by :setlocal isk+=$:

patch.diff
diff --git a/runtime/ftplugin/java.vim b/runtime/ftplugin/java.vim
index cfd25bc..fd97eed 100644
--- a/runtime/ftplugin/java.vim
+++ b/runtime/ftplugin/java.vim
@@ -3,7 +3,7 @@
 " Maintainer:		Aliaksei Budavei <0x000c70 AT gmail DOT com>
 " Former Maintainer:	Dan Sharp
 " Repository:		https://github.com/zzzyxwvut/java-vim.git
-" Last Change:		2024 Dec 25
+" Last Change:		2025 Apr 26
 "			2024 Jan 14 by Vim Project (browsefilter)
 "			2024 May 23 by Riley Bruins <[email protected]> ('commentstring')
 
@@ -30,6 +30,31 @@ let b:did_ftplugin = 1
 " extension.
 set suffixes+=.class
 
+" Set up "&define" and "&include".
+let s:peek = ''
+
+try
+    " Since v7.3.1037.
+    if 'ab' !~ 'a\@1<!b'
+	let s:peek = string(strlen('instanceof') + 8)
+    endif
+catch /\<E59:/
+endtry
+
+" Treat "s:common" as a non-backtracking unit to avoid matching constructor
+" declarations whose package-private headers are indistinguishable from method
+" invocation.  Note that "[@-]" must not and "$" may not be in "&l:iskeyword".
+let s:common = '\%(\%(\%(@\%(interface\)\@!\%(\K\k*\.\)*\K\k*\)\s\+\)*' .
+	\ '\%(p\%(rivate\|rotected\|ublic\)\s\+\)\=\)\@>'
+let s:types = '\%(\%(abstract\|final\|non-sealed\|s\%(ealed\|tatic\|trictfp\)\)\s\+\)*' .
+	\ '\%(class\|enum\|@\=interface\|record\)\s\+\ze\K\k*\>'
+let s:methods = '\%(\%(abstract\|default\|final\|native\|s\%(tatic\|trictfp\|ynchronized\)\)\s\+\)*' .
+	\ '\%(<.\{-1,}>\s\+\)\=\%(\K\k*\.\)*\K\k*\s*\%(<.\{-1,}>\%(\s\|\[\)\@=\)\=\s*\%(\[\]\s*\)*' .
+	\ '\s\+\ze\%(\<\%(case\|instanceof\)\s\+\)\@' . s:peek . '<!\K\k*\s*('
+let &l:define = printf('\C\m^\s*%s\%%(%s\|%s\)', s:common, s:types, s:methods)
+let &l:include = '\C\m^\s*import\s\+\ze\%(\K\k*\.\)*\K\k*;'
+unlet s:methods s:types s:common s:peek
+
 " Enable gf on import statements.  Convert . in the package
 " name to / and append .java to the name, then search the path.
 setlocal includeexpr=substitute(v:fname,'\\.','/','g')
@@ -341,7 +366,7 @@ if (!empty(get(g:, 'spotbugs_properties', {})) ||
 endif
 
 function! JavaFileTypeCleanUp() abort
-    setlocal suffixes< suffixesadd< formatoptions< comments< commentstring< path< includeexpr<
+    setlocal suffixes< suffixesadd< formatoptions< comments< commentstring< path< includeexpr< include< define<
     unlet! b:browsefilter
 
     " The concatenated ":autocmd" removals may be misparsed as an ":autocmd".

Despite our efforts, some KNOWN QUIRKS of [d etc. are hard
to accommodate:

  • Finding declarations depends on repetitive fiddling with
    &path and/or :lcd -- you don't want to waste resources
    on looking in places irrelevant to your current query. It
    may not be that obvious, especially when some local items
    from a project file are requested in a test file and there
    are distinct directory trees for project sources and test
    sources.

  • Finding declarations of member types of the java.lang
    package appears to be out of reach, there are no import
    declarations to look up.

  • Finding declarations of member types of the same package
    but from distinct compilation units (i.e. files) appears
    to be out of reach, there are no import declarations to
    look up.

  • Fully-qualified types that are not redundantly import'd
    must be looked up with gf and NOT [d.

  • When a variable (or a keyword, e.g. yield) and a method
    share one name, such variable references will point to its
    namesake method declaration.

zzzyxwvut added a commit to zzzyxwvut/vimvim that referenced this pull request May 8, 2025
…def"

=============== LIMITATIONS AND OBSERVATIONS ===============

* Remember that external-type names can only be found when
  they match filenames resolvable in "&path" with "import"
  declarations; load the source file of an external type to
  look up its nested types and sibling top types, if any.

* Strive to narrow the search by assigning only relevant
  pathnames for directories *or* an archive to "&path", e.g.
  ":set path-=/usr/include".

* Use "{Visual}gf" on fully-qualified names.

* Accept the fact that "&define" cannot contain end-of-line
  characters (":help definition-search").  A declaration
  whose matchable header is not contained within a line can
  be found iff all of its non-optional components belong to
  the same line; for types, such components are a keyword,
  e.g. "class", followed by a run of blank characters and
  an identifier, e.g. "Test"; for methods: a return type,
  e.g. "String", or a keyword "void", followed by a run of
  blank characters and an identifier, e.g. "toString", that
  is followed by "(".

* The members of the "java.lang" package are usually not
  associated with "import" declarations; to look up their
  declarations, load a source file for a member of that
  package, and then use, on a simple name of interest for
  a member, either "[-Ctrl-d" etc. for local declarations
  or "gf" for external declarations, assuming that "." *or*
  the appropriate pathname for a JDK archive is assigned to
  "&path".

* Follow the above instruction made for the "java.lang"
  members for any type whose simple name is not associated
  with an "import" declaration, i.e. a member type of the
  same package that is declared in another compilation unit.

* Append the "$" character to "&iskeyword" when looking up
  declarations of generated code.

See zzzyxwvut/java-vim#4.


Co-authored-by: Konfekt <[email protected]>
Signed-off-by: Aliaksei Budavei <[email protected]>
zzzyxwvut added a commit to zzzyxwvut/vimvim that referenced this pull request May 9, 2025
…def"

=============== LIMITATIONS AND OBSERVATIONS ===============

* Remember that external-type names can only be found when
  they match filenames resolvable in "&path" with "import"
  declarations; load the source file of an external type to
  look up its nested types and sibling top types, if any.

* Strive to narrow the search by assigning only relevant
  pathnames for directories *or* an archive to "&path", e.g.
  ":set path-=/usr/include".

* Use "{Visual}gf" on fully-qualified names.

* Accept the fact that "&define" cannot contain end-of-line
  characters (":help definition-search").  A declaration
  whose matchable header is not contained within a line can
  be found iff all of its non-optional components belong to
  the same line; for types, such components are a keyword,
  e.g. "class", followed by a run of blank characters and
  an identifier, e.g. "Test"; for methods: a return type,
  e.g. "String", or a keyword "void", followed by a run of
  blank characters and an identifier, e.g. "toString", that
  is followed by "(".

* The members of the "java.lang" package are usually not
  associated with "import" declarations; to look up their
  declarations, load a source file for a member of that
  package, and then use, on a simple name of interest for
  a member, either "[-Ctrl-d" etc. for local declarations
  or "gf" for external declarations, assuming that "." *or*
  the appropriate pathname for a JDK archive is assigned to
  "&path".

* Follow the above instruction made for the "java.lang"
  members for any type whose simple name is not associated
  with an "import" declaration, i.e. a member type of the
  same package that is declared in another compilation unit.

* Append the "$" character to "&iskeyword" when looking up
  declarations of generated code.

See zzzyxwvut/java-vim#4.


Co-authored-by: Konfekt <[email protected]>
Signed-off-by: Aliaksei Budavei <[email protected]>
chrisbra pushed a commit to vim/vim that referenced this pull request May 10, 2025
…def"

=============== LIMITATIONS AND OBSERVATIONS ===============

* Remember that external-type names can only be found when
  they match filenames resolvable in "&path" with "import"
  declarations; load the source file of an external type to
  look up its nested types and sibling top types, if any.

* Strive to narrow the search by assigning only relevant
  pathnames for directories *or* an archive to "&path", e.g.
  ":set path-=/usr/include".

* Use "{Visual}gf" on fully-qualified names.

* Accept the fact that "&define" cannot contain end-of-line
  characters (":help definition-search").  A declaration
  whose matchable header is not contained within a line can
  be found iff all of its non-optional components belong to
  the same line; for types, such components are a keyword,
  e.g. "class", followed by a run of blank characters and
  an identifier, e.g. "Test"; for methods: a return type,
  e.g. "String", or a keyword "void", followed by a run of
  blank characters and an identifier, e.g. "toString", that
  is followed by "(".

* The members of the "java.lang" package are usually not
  associated with "import" declarations; to look up their
  declarations, load a source file for a member of that
  package, and then use, on a simple name of interest for
  a member, either "[-Ctrl-d" etc. for local declarations
  or "gf" for external declarations, assuming that "." *or*
  the appropriate pathname for a JDK archive is assigned to
  "&path".

* Follow the above instruction made for the "java.lang"
  members for any type whose simple name is not associated
  with an "import" declaration, i.e. a member type of the
  same package that is declared in another compilation unit.

* Append the "$" character to "&iskeyword" when looking up
  declarations of generated code.

See zzzyxwvut/java-vim#4.

closes: #17281

Co-authored-by: Konfekt <[email protected]>
Signed-off-by: Aliaksei Budavei <[email protected]>
Signed-off-by: Christian Brabandt <[email protected]>
=============== LIMITATIONS AND OBSERVATIONS ===============

* Remember that external-type names can only be found when
  they match filenames resolvable in "&path" with "import"
  declarations; load the source file of an external type to
  look up its nested types and sibling top types, if any.

* Strive to narrow the search by assigning only relevant
  pathnames for directories *or* an archive to "&path", e.g.
  ":set path-=/usr/include".

* Use "{Visual}gf" on fully-qualified names.

* Accept the fact that "&define" cannot contain end-of-line
  characters (":help definition-search").  A declaration
  whose matchable header is not contained within a line can
  be found iff all of its non-optional components belong to
  the same line; for types, such components are a keyword,
  e.g. "class", followed by a run of blank characters and
  an identifier, e.g. "Test"; for methods: a return type,
  e.g. "String", or a keyword "void", followed by a run of
  blank characters and an identifier, e.g. "toString", that
  is followed by "(".

* The members of the "java.lang" package are usually not
  associated with "import" declarations; to look up their
  declarations, load a source file for a member of that
  package, and then use, on a simple name of interest for
  a member, either "[-Ctrl-d" etc. for local declarations
  or "gf" for external declarations, assuming that "." *or*
  the appropriate pathname for a JDK archive is assigned to
  "&path".

* Follow the above instruction made for the "java.lang"
  members for any type whose simple name is not associated
  with an "import" declaration, i.e. a member type of the
  same package that is declared in another compilation unit.

* Append the "$" character to "&iskeyword" when looking up
  declarations of generated code.


Co-authored-by: Aliaksei Budavei <[email protected]>
@zzzyxwvut
Copy link
Owner

Can you please rebase your patch-1 branch as follows:

git switch patch-1
git reset --hard b98331c

git fetch https://github.com/zzzyxwvut/java-vim prs/4
git rebase FETCH_HEAD

git push origin --force-with-lease patch-1

so I can merge it into master?

clason added a commit to clason/neovim that referenced this pull request May 11, 2025
… with "&inc" and "&def"

=============== LIMITATIONS AND OBSERVATIONS ===============

* Remember that external-type names can only be found when
  they match filenames resolvable in "&path" with "import"
  declarations; load the source file of an external type to
  look up its nested types and sibling top types, if any.

* Strive to narrow the search by assigning only relevant
  pathnames for directories *or* an archive to "&path", e.g.
  ":set path-=/usr/include".

* Use "{Visual}gf" on fully-qualified names.

* Accept the fact that "&define" cannot contain end-of-line
  characters (":help definition-search").  A declaration
  whose matchable header is not contained within a line can
  be found iff all of its non-optional components belong to
  the same line; for types, such components are a keyword,
  e.g. "class", followed by a run of blank characters and
  an identifier, e.g. "Test"; for methods: a return type,
  e.g. "String", or a keyword "void", followed by a run of
  blank characters and an identifier, e.g. "toString", that
  is followed by "(".

* The members of the "java.lang" package are usually not
  associated with "import" declarations; to look up their
  declarations, load a source file for a member of that
  package, and then use, on a simple name of interest for
  a member, either "[-Ctrl-d" etc. for local declarations
  or "gf" for external declarations, assuming that "." *or*
  the appropriate pathname for a JDK archive is assigned to
  "&path".

* Follow the above instruction made for the "java.lang"
  members for any type whose simple name is not associated
  with an "import" declaration, i.e. a member type of the
  same package that is declared in another compilation unit.

* Append the "$" character to "&iskeyword" when looking up
  declarations of generated code.

See zzzyxwvut/java-vim#4.

closes: vim/vim#17281

vim/vim@7344024

Co-authored-by: Aliaksei Budavei <[email protected]>
Co-authored-by: Konfekt <[email protected]>
clason added a commit to neovim/neovim that referenced this pull request May 11, 2025
… with "&inc" and "&def"

=============== LIMITATIONS AND OBSERVATIONS ===============

* Remember that external-type names can only be found when
  they match filenames resolvable in "&path" with "import"
  declarations; load the source file of an external type to
  look up its nested types and sibling top types, if any.

* Strive to narrow the search by assigning only relevant
  pathnames for directories *or* an archive to "&path", e.g.
  ":set path-=/usr/include".

* Use "{Visual}gf" on fully-qualified names.

* Accept the fact that "&define" cannot contain end-of-line
  characters (":help definition-search").  A declaration
  whose matchable header is not contained within a line can
  be found iff all of its non-optional components belong to
  the same line; for types, such components are a keyword,
  e.g. "class", followed by a run of blank characters and
  an identifier, e.g. "Test"; for methods: a return type,
  e.g. "String", or a keyword "void", followed by a run of
  blank characters and an identifier, e.g. "toString", that
  is followed by "(".

* The members of the "java.lang" package are usually not
  associated with "import" declarations; to look up their
  declarations, load a source file for a member of that
  package, and then use, on a simple name of interest for
  a member, either "[-Ctrl-d" etc. for local declarations
  or "gf" for external declarations, assuming that "." *or*
  the appropriate pathname for a JDK archive is assigned to
  "&path".

* Follow the above instruction made for the "java.lang"
  members for any type whose simple name is not associated
  with an "import" declaration, i.e. a member type of the
  same package that is declared in another compilation unit.

* Append the "$" character to "&iskeyword" when looking up
  declarations of generated code.

See zzzyxwvut/java-vim#4.

closes: vim/vim#17281

vim/vim@7344024

Co-authored-by: Aliaksei Budavei <[email protected]>
Co-authored-by: Konfekt <[email protected]>
@zzzyxwvut zzzyxwvut merged commit c19f3c5 into zzzyxwvut:master May 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants