Defining your own syntax highlighting in Crucible or Fisheye
Platform Notice: Data Center Only - This article only applies to Atlassian products on the Data Center platform.
Note that this KB was created for the Data Center version of the product. Data Center KBs for non-Data-Center-specific features may also work for Server versions of the product, however they have not been tested. Support for Server* products ended on February 15th 2024. If you are running a Server product, you can visit the Atlassian Server end of support announcement to review your migration options.
*Except Fisheye and Crucible
Creating your own
All existing syntax highlighting definitions can be found at FISHEYE_INST/syntax
directory. For example, java.def
:
java.def
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
syntaxdef java {
/\s+/m : ;
/(a(bstract|ssert)|b(oolean|reak|yte)|c(a(se|tch)|har|lass|on(st|tinue))\
|d(efault|o(|uble))|e(lse|xtends|num)|f(alse|inal(|ly)|loat|or)|goto\
|i(f|mp(lements|ort)|n(stanceof|t(|erface)))|long|n(ative|ew|ull)\
|p(ackage|r(ivate|otected)|ublic)|return|s(hort|t(atic|rictfp)|uper|witch|ynchronized)\
|t(h(is|row(|s))|r(ansient|ue|y))|vo(id|latile)|while)\b/ : {
region {
type=keyword;
index=word;
}
}
/[\p{L}_\$][\p{L}\p{N}_\$]*/ : {
region {
type=identifier;
index=word;
}
}
#annotation
/@[\p{L}_\$][\p{L}\p{N}_\$]*/ : {
region {
type=annotation;
index=word;
}
}
# string literals
/"/ : {
context {
/\\"/: ;
/\\./: ;
/$/m : exit;
"\"" : exit;
}
region ${ALL} {
type=string;
}
}
# char literal
/'/ : {
context {
/\\./: ;
/$/m : exit;
"'" : exit;
}
region ${ALL} {
type=char_literal;
}
}
/\/\/.*$/m : {
todo(${0});
region {
type=comment;
index=prose;
findlinks=true;
}
}
/\/\*\*(.*?)\*\//s : {
javadoc(${1});
region {
type=comment;
index=prose;
findlinks=true;
}
}
context javadoc {
/(\B@[a-zA-Z0-9-_.]+)\s*([^@\*]*)/ : {
region ${1} {
type=commentmeta;
}
}
}
/(0x[0-9a-f][0-9a-f_]*l?)|(0b[0-1][0-1_]*l?)|([0-9][0-9_]*(\.[0-9][0-9_]*)?|\.[0-9][0-9_]+)(e(\+|-)?[0-9][0-9_]*)?(f|l|d)?/i : {
region {
type=numeric;
}
}
/\/\*(.*?)\*\//s : {
todo(${1});
region {
type=comment;
findlinks=true;
}
region ${1} {
index=prose;
}
}
context todo {
/(?i)todo\s.*/ : {
region {
type=todo;
}
}
}
}
As can be seen in above, these syntax highlighting definition files are just Regular Expressions, so if you know Regular Expressions and the semantics of the programming language you want to write a new definition for, you should be able to translate one of these existing definitions to what you need in your new file. Usually there is no need to create your own definition from scratch for a programming language that is not already there, you can use an existing template that best matches the definition you need as a starting point.
Make sure to following these steps:
Copy one of the existing definitions into your
FISHEYE_INST/syntax
directory. (Note, by making your modifications in the FISHEYE_INST directory, your changes will persist across upgrades. If you make the changes in FISHEYE_HOME, they will need to be reapplied each upgrade).Modify the filename and the definition in the
FISHEYE_INST/syntax
directory. E.g. you can changejava.def
toobjectiveC.def
and modify the definition to suit your programming language.Copy the
FISHEYE_HOME/syntax/filename.map
to yourFISHEYE_INST/syntax
directory and modify it to map file extensions to definitions.Restart Fisheye to pick up the change.
Grammar
This is a sample grammar of a .def
file:
Grammar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
syntaxdef : "syntaxdef" identifier LCURLY ( matchrule | context )* RCURLY;
context : "context" identifier LCURLY contextBody RCURLY;
contextBody : ( matchrule | context )*;
matchrule : ( stringmatcher | regexpmatcher ) COLON matchbody;
stringmatcher: STRING_LITERAL;
regexpmatcher : REGEXP;
matchbody :
( ("exit")? SEMI)
|
(
LCURLY
("predicate" ASSIGN dottedIdentifier)?
(
( ("exit")? SEMI)
|
(
((
(identifier (LPAREN submatchref RPAREN)? SEMI)
|
("context" (LPAREN submatchref RPAREN)? LCURLY contextBody RCURLY)
))*
(regiondecl)*
)
)
("filter" ASSIGN dottedIdentifier)*
RCURLY
)
;
regiondecl
: "region" (submatchref)? LCURLY
(
identifier (DOT identifier)* ASSIGN
(identifier|STRING_LITERAL) SEMI
)*
RCURLY;
submatchref : DOLLAR LCURLY (identifier|NUMINT) RCURLY;
dottedIdentifier : IDENT ("." IDENT)*;
LPAREN : '(' ;
RPAREN : ')' ;
LCURLY : '{' ;
RCURLY : '}' ;
COLON : ':' ;
SEMI : ';' ;
DOT : '.' ;
ASSIGN : '=' ;
Examples
Below are some examples found online and / or shared by customers:
TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Basic TypeScript syntax highlighting rules for FishEye/Crucible.
#
# Put this file to the <FISHEYE_INST>/syntax directory and update the
# <FISHEYE_INST>/syntax/filename.map file to contain the following lines:
#
# "**/*.ts" typescript.def "TypeScript"
#
# FishEye/Crucible might need to be restarted to have the changes take effect.
#
# References:
# * https://confluence.atlassian.com/display/FISHKB/Defining+your+own+syntax+highlighting+in+Crucible+or+Fisheye
# * https://confluence.atlassian.com/display/FISHKB/Configure+syntax+highlighting+for+non-standard+file+extensions
#
# Notes:
# * this schema considers identifiers that start with an uppercase letter to represent classes or constants, so
# it treats such identifiers as 'symbol' for better colors. If you don't want such treatment, replace the
# 'symbol' with 'identifier'.
#
# TODO: multiline strings do not really work, e.g.:
# var mySuperString = 'this is a \
# multiline string';
syntaxdef typescript {
# whitespaces
/\s+/m : ;
# keywords
/\b(a(ny|s)|b(oolean|reak)|c(a(se|tch)|lass|on(st(|ructor)|tinue))|d(e(bugger|clare|fault|lete)|o)|e(lse|num|x(port|tends))|f(alse|inally|or|rom|unction)|get|i(f|mp(lements|ort)|n(|stanceof|terface))|let|module|n(ew|u(ll|mber))|of|p(ackage|r(ivate|otected)|ublic)|r(equire|eturn)|s(et|t(atic|ring)|uper|witch|ymbol)|t(h(is|row)|r(ue|y)|ype(|of))|v(ar|oid)|w(hile|ith)|yield)\b/ : {
region {
type=keyword;
index=word;
}
}
# decorators
/@[a-zA-Z_][a-zA-Z0-9_]*/ : {
region {
type=decorator;
index=word;
}
}
# backquote string literal
/`/ : {
context {
/\\`/: ;
"`" : exit;
/(\$\{.*?\})/ : {
region ${1} {
type=identifier;
index=word;
}
}
}
region ${ALL} {
type=string;
}
}
# single-quote string literal
/'/ : {
context {
/\\'/: ;
/$/m : exit;
"'" : exit;
}
region ${ALL} {
type=string;
}
}
# double-quote string literal
/"/ : {
context {
/\\"/: ;
/$/m : exit;
"\"" : exit;
}
region ${ALL} {
type=string;
}
}
# numbers
/(0x[0-9a-f]+)|(([0-9]+\.?)|([0-9]*\.[0-9]+))/i : {
region {
type=numeric;
}
}
# Identifiers
/[a-z_$][a-zA-Z_0-9]*/ : {
region {
type=identifier;
index=word;
}
}
# probable classes or constant expressions
/[A-Z][a-zA-Z_0-9]*/ : {
region {
type=symbol;
index=word;
}
}
# multiline comment
/\/\*(.*?)\*\//s : {
todo(${1});
region {
type=comment;
findlinks=true;
}
region ${1} {
index=prose;
}
}
# one-line comment
/\/\/.*$/m : {
todo(${0});
region {
type=comment;
index=prose;
findlinks=true;
}
}
context todo {
/(?i)(todo):?\s.*/ : {
region {
type=todo;
}
}
}
# regex
/
\/
([^\n\r*\\\/]|\\[^\r\n])# RegularExpressionFirstChar
/x : {
context { # RegularExpresionChar*
/\\[^\r\n]/ : ;
/\// : exit;
/[\r\n]/ : exit; # for safety
}
region ${ALL} {
type=regex;
}
}
}
Chapel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
syntaxdef chpl {
# whitespace
/\s+/m : ;
# keywords
/(align|as|atomic|\
begin|bool|borrowed|break|by|\
catch|class|clone|cobegin|coforall|complex|config|const|continue|\
defer|delete|destroy|dmapped|domain|do|\
else|enum|except|export|extern|\
false|forall|forwarding|for|\
if|imag|index|init|inline|inout|integral|int|in|iter|\
label|lambda|let|lifetime|locale|local|\
module|\
new|nil|noinit|\
only|on|opaque|otherwise|out|override|owned|\
param|pragma|primitive|private|proc|prototype|public|\
range|real|record|reduce|ref|require|return|\
scan|select|serial|shared|single|sparse|string|subdomain|sync|\
then|this|throws|throw|true|try|type|\
uint|union|unmanaged|use|\
var|\
when|where|while|with|\
yield|\
zip)\b/ : {
region {
type=keyword;
index=word;
}
}
# preprocessor line
/^#.*$/m : {
region {
type=keyword; # obviously not a keyword. this will change when we shake out a cannonical list of types
}
}
# char literal
/L?'(\\.|\\[0-7]{3}|\\x[0-9a-fA-F]{2}|.)'/ : {
region {
type=char_literal;
}
}
# string literal
/L?"/ : {
context {
/\\"/: ;
/\\./: ;
/$/m : exit;
"\"" : exit;
}
region ${ALL} {
type=string;
}
}
#numeric literal
/(0x[0-9a-f]+(lu|ul|[ul])?)|((([0-9]+\.?)|([0-9]*\.[0-9]+))(e(\+|-)?[0-9]+)?(fl|lf|[lf])?)/i : {
region {
type=numeric;
}
}# identifier
/[a-zA-Z_][a-zA-Z_0-9]*/ : {
region {
type=identifier;
index=word;
}
}
# comment
/\/\*.*?\*\//s : {
region {
type=comment;
index=prose;
findlinks=true;
}
}
# inline comment
/\/\/.*$/m : {
region {
type=comment;
index=prose;
findlinks=true;
}
}
}
Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
syntaxdef kotlin {
/\s+/m : ;
#keywords
/(a(bstract|nnotation|s)|break|c(atch|lass|o(mpanion|n(st|tinue))|rossinline)\
|d(ata|elegate|o|ynamic)|e(lse|num|xternal)|f(i(eld|le|nally)|or|un|alse)|get\
|i(f|mport|n(fix|it|line|terface)|s|t)|n(oinline|ull)|o(bject|pe(n|rator)|ut)\
|p(a(ckage|ram)|roperty)|re(ceiver|ified|turn)|s(e(aled|tparam)|u(per|spend))\
|t(ailrec|h(is|row)|r(y|ue)|ype(alias|of))|va(l|rarg)|wh(en|ile))\b/ : {
region {
type=keyword;
index=word;
}
}
/[\p{L}_\$][\p{L}\p{N}_\$]*/ : {
region {
type=identifier;
index=word;
}
}
#annotation
/@[\p{L}_\$][\p{L}\p{N}_\$]*/ : {
region {
type=annotation;
index=word;
}
}
# string literals
/"/ : {
context {
/\\"/: ;
/\\./: ;
/$/m : exit;
"\"" : exit;
}
region ${ALL} {
type=string;
}
}
# char literal
/'/ : {
context {
/\\./: ;
/$/m : exit;
"'" : exit;
}
region ${ALL} {
type=char_literal;
}
}
/\/\/.*$/m : {
todo(${0});
region {
type=comment;
index=prose;
findlinks=true;
}
}
/\/\*\*(.*?)\*\//s : {
javadoc(${1});
region {
type=comment;
index=prose;
findlinks=true;
}
}
context javadoc {
/(\B@[a-zA-Z0-9-_.]+)\s*([^@\*]*)/ : {
region ${1} {
type=commentmeta;
}
}
}
/(0x[0-9a-f][0-9a-f_]*l?)|(0b[0-1][0-1_]*l?)|([0-9][0-9_]*(\.[0-9][0-9_]*)?|\.[0-9][0-9_]+)(e(\+|-)?[0-9][0-9_]*)?(f|l|d)?/i : {
region {
type=numeric;
}
}
/\/\*(.*?)\*\//s : {
todo(${1});
region {
type=comment;
findlinks=true;
}
region ${1} {
index=prose;
}
}
context todo {
/(?i)todo\s.*/ : {
region {
type=todo;
}
}
}
}
Related
Configure syntax highlighting for non-standard file extensions
Was this helpful?