Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enh(java) support functions with nested template types #3074

Merged
merged 3 commits into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Grammars:

- chore(properties) disable auto-detection #3102 [Josh Goebel][]
- fix(properties) fix incorrect handling of non-alphanumeric keys #3102 [Egor Rogov][]
- enh(java) support functions with nested template types (#2641) [Josh Goebel][]
- enh(java) highlight types and literals separate from keywords (#3074) [Josh Goebel][]
- enh(shell) add alias ShellSession [Ryan Mulligan][]
- enh(shell) consider one space after prompt as part of prompt [Ryan Mulligan][]
- fix(nginx) fix bug with $ and @ variables [Josh Goebel][]
Expand Down
145 changes: 119 additions & 26 deletions src/languages/java.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,116 @@ Category: common, enterprise
Website: https://www.java.com/
*/

import { NUMERIC } from "./lib/java.js";
import {
NUMERIC as NUMBER
} from "./lib/java.js";

/**
* Allows recursive regex expressions to a given depth
*
* ie: recurRegex("(abc~~~)", /~~~/g, 2) becomes:
* (abc(abc(abc)))
*
* @param {string} re
* @param {RegExp} substitution (should be a g mode regex)
* @param {number} depth
* @returns {string}``
*/
function recurRegex(re, substitution, depth) {
if (depth === -1) return "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (depth === -1) return "";
if (depth < 0) return "";

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is an improvement. I'd rather be precise.


return re.replace(substitution, _ => {
return recurRegex(re, substitution, depth - 1);
});
}

/** @type LanguageFn */
export default function(hljs) {
var JAVA_IDENT_RE = '[\u00C0-\u02B8a-zA-Z_$][\u00C0-\u02B8a-zA-Z_$0-9]*';
var GENERIC_IDENT_RE = JAVA_IDENT_RE + '(<' + JAVA_IDENT_RE + '(\\s*,\\s*' + JAVA_IDENT_RE + ')*>)?';
var KEYWORDS = 'false synchronized int abstract float private char boolean var static null if const ' +
'for true while long strictfp finally protected import native final void ' +
'enum else break transient catch instanceof byte super volatile case assert short ' +
'package default double public try this switch continue throws protected public private ' +
'module requires exports do';
const JAVA_IDENT_RE = '[\u00C0-\u02B8a-zA-Z_$][\u00C0-\u02B8a-zA-Z_$0-9]*';
const GENERIC_IDENT_RE = JAVA_IDENT_RE +
recurRegex('(<' + JAVA_IDENT_RE + '~~~(\\s*,\\s*' + JAVA_IDENT_RE + '~~~)*>)?', /~~~/g, 2);
const MAIN_KEYWORDS = [
'synchronized',
'abstract',
'private',
'var',
'static',
'if',
'const ',
'for',
'while',
'strictfp',
'finally',
'protected',
'import',
'native',
'final',
'void',
'enum',
'else',
'break',
'transient',
'catch',
'instanceof',
'super',
'volatile',
'case',
'assert',
'package',
'default',
'public',
'try',
'this',
'switch',
'continue',
'throws',
'protected',
'public',
'private',
'module',
'requires',
'exports',
'do'
];

var ANNOTATION = {
const LITERALS = [
'false',
'true',
'null'
];

const TYPES = [
'char',
'boolean',
'long',
'float',
'int',
'byte',
'short',
'double'
];

const KEYWORDS = {
keyword: MAIN_KEYWORDS,
literal: LITERALS,
type: TYPES
};

const ANNOTATION = {
className: 'meta',
begin: '@' + JAVA_IDENT_RE,
contains: [
{
begin: /\(/,
end: /\)/,
contains: ["self"] // allow nested () inside our annotation
},
contains: [ "self" ] // allow nested () inside our annotation
}
]
};
const NUMBER = NUMERIC;

return {
name: 'Java',
aliases: ['jsp'],
aliases: [ 'jsp' ],
keywords: KEYWORDS,
illegal: /<\/|#/,
contains: [
Expand All @@ -43,7 +126,8 @@ export default function(hljs) {
contains: [
{
// eat up @'s in emails to prevent them to be recognized as doctags
begin: /\w+@/, relevance: 0
begin: /\w+@/,
relevance: 0
},
{
className: 'doctag',
Expand All @@ -64,7 +148,9 @@ export default function(hljs) {
hljs.QUOTE_STRING_MODE,
{
className: 'class',
beginKeywords: 'class interface enum', end: /[{;=]/, excludeEnd: true,
beginKeywords: 'class interface enum',
end: /[{;=]/,
excludeEnd: true,
// TODO: can this be removed somehow?
// an extra boost because Java is more popular than other languages with
// this same syntax feature (this is just to preserve our tests passing
Expand All @@ -73,7 +159,9 @@ export default function(hljs) {
keywords: 'class interface enum',
illegal: /[:"\[\]]/,
contains: [
{ beginKeywords: 'extends implements' },
{
beginKeywords: 'extends implements'
},
hljs.UNDERSCORE_TITLE_MODE
]
},
Expand All @@ -91,40 +179,45 @@ export default function(hljs) {
end: /[{;=]/,
keywords: KEYWORDS,
contains: [
{ beginKeywords: "record" },
{
beginKeywords: "record"
},
{
begin: hljs.UNDERSCORE_IDENT_RE + '\\s*\\(',
returnBegin: true,
relevance: 0,
contains: [hljs.UNDERSCORE_TITLE_MODE]
contains: [ hljs.UNDERSCORE_TITLE_MODE ]
},
{
className: 'params',
begin: /\(/, end: /\)/,
begin: /\(/,
end: /\)/,
keywords: KEYWORDS,
relevance: 0,
contains: [
hljs.C_BLOCK_COMMENT_MODE
]
contains: [ hljs.C_BLOCK_COMMENT_MODE ]
},
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE
]
},
{
className: 'function',
begin: '(' + GENERIC_IDENT_RE + '\\s+)+' + hljs.UNDERSCORE_IDENT_RE + '\\s*\\(', returnBegin: true, end: /[{;=]/,
begin: '(' + GENERIC_IDENT_RE + '\\s+)+' + hljs.UNDERSCORE_IDENT_RE + '\\s*\\(',
returnBegin: true,
end: /[{;=]/,
excludeEnd: true,
keywords: KEYWORDS,
contains: [
{
begin: hljs.UNDERSCORE_IDENT_RE + '\\s*\\(', returnBegin: true,
begin: hljs.UNDERSCORE_IDENT_RE + '\\s*\\(',
returnBegin: true,
relevance: 0,
contains: [hljs.UNDERSCORE_TITLE_MODE]
contains: [ hljs.UNDERSCORE_TITLE_MODE ]
},
{
className: 'params',
begin: /\(/, end: /\)/,
begin: /\(/,
end: /\)/,
keywords: KEYWORDS,
relevance: 0,
contains: [
Expand Down
16 changes: 8 additions & 8 deletions test/api/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('.highlight()', () => {
it('should support ignoreIllegals (old API)', () => {
let code = "float # float";
let result = hljs.highlight("java", code, true);
result.value.should.equal(`<span class="hljs-keyword">float</span> # <span class="hljs-keyword">float</span>`);
result.value.should.equal(`<span class="hljs-type">float</span> # <span class="hljs-type">float</span>`);

code = "float # float";
result = hljs.highlight("java", code, false);
Expand All @@ -17,7 +17,7 @@ describe('.highlight()', () => {
it('should support ignoreIllegals (new API)', () => {
let code = "float # float";
let result = hljs.highlight(code, { language: "java", ignoreIllegals: true });
result.value.should.equal(`<span class="hljs-keyword">float</span> # <span class="hljs-keyword">float</span>`);
result.value.should.equal(`<span class="hljs-type">float</span> # <span class="hljs-type">float</span>`);

code = "float # float";
result = hljs.highlight(code, { language: "java", ignoreIllegals: false });
Expand All @@ -37,9 +37,9 @@ describe('.highlight()', () => {
result.value.should.equal(
'<span class="hljs-function"><span class="hljs-keyword">public</span> ' +
'<span class="hljs-keyword">void</span> <span class="hljs-title">moveTo</span>' +
'<span class="hljs-params">(<span class="hljs-keyword">int</span> x, ' +
'<span class="hljs-keyword">int</span> y, ' +
'<span class="hljs-keyword">int</span> z)</span></span>;'
'<span class="hljs-params">(<span class="hljs-type">int</span> x, ' +
'<span class="hljs-type">int</span> y, ' +
'<span class="hljs-type">int</span> z)</span></span>;'
);
});
it('should works without continuation', () => {
Expand All @@ -49,9 +49,9 @@ describe('.highlight()', () => {
result.value.should.equal(
'<span class="hljs-function"><span class="hljs-keyword">public</span> ' +
'<span class="hljs-keyword">void</span> <span class="hljs-title">moveTo</span>' +
'<span class="hljs-params">(<span class="hljs-keyword">int</span> x, ' +
'<span class="hljs-keyword">int</span> y, ' +
'<span class="hljs-keyword">int</span> z)</span></span>;'
'<span class="hljs-params">(<span class="hljs-type">int</span> x, ' +
'<span class="hljs-type">int</span> y, ' +
'<span class="hljs-type">int</span> z)</span></span>;'
);
});
});
2 changes: 1 addition & 1 deletion test/markup/java/annotations.expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Example</span> </span>{
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">foo</span><span class="hljs-params">(<span class="hljs-meta">@SuppressWarnings(&quot;unused&quot;)</span> <span class="hljs-keyword">int</span> bar)</span> </span>{ }
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">foo</span><span class="hljs-params">(<span class="hljs-meta">@SuppressWarnings(&quot;unused&quot;)</span> <span class="hljs-type">int</span> bar)</span> </span>{ }
}
5 changes: 5 additions & 0 deletions test/markup/java/functions.expect.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> &lt;A,B,C&gt; <span class="hljs-function">Tuple&lt;A,B,C&gt; <span class="hljs-title">fun</span><span class="hljs-params">(Future&lt;Tuple&lt;A,B,C&gt;&gt; future)</span> <span class="hljs-keyword">throws</span> Exceptions </span>{
}

<span class="hljs-function"><span class="hljs-keyword">static</span> Optional&lt;List&lt;Token&gt;&gt; <span class="hljs-title">parseAll</span><span class="hljs-params">(String s)</span> </span>{
}
5 changes: 5 additions & 0 deletions test/markup/java/functions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public static <A,B,C> Tuple<A,B,C> fun(Future<Tuple<A,B,C>> future) throws Exceptions {
}

static Optional<List<Token>> parseAll(String s) {
}
Loading