mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-15 13:29:40 +02:00
1179 Add ability to use generics for members
Created new class to handle parsing of members with regex to handle determining type of member and the different elements within. Also moved addTSpan in drawClass method to this new file. Finally, I added a "catch all" section in case something fails in the regex to make sure everything gets formatted correctly. Added more tests and documentation updating gitignore Tired of constantly having to ignore files and stash/pop when switching between branches
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -12,3 +12,9 @@ yarn-error.log
|
|||||||
token
|
token
|
||||||
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
dist/classTest.html
|
||||||
|
|
||||||
|
dist/sequenceTest.html
|
||||||
|
|
||||||
|
.vscode/
|
||||||
|
@@ -289,4 +289,20 @@ describe('Class diagram', () => {
|
|||||||
);
|
);
|
||||||
cy.get('svg');
|
cy.get('svg');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('12: should render a simple class diagram with generic types', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
classDiagram
|
||||||
|
class Class10~T~ {
|
||||||
|
int[] id
|
||||||
|
List~int~ ids
|
||||||
|
test(List~int~ ids) List~bool~
|
||||||
|
testArray() bool[]
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
cy.get('svg');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -105,7 +105,7 @@ Naming convention: a class name should be composed of alphanumeric (unicode allo
|
|||||||
|
|
||||||
UML provides mechanisms to represent class members, such as attributes and methods, and additional information about them.
|
UML provides mechanisms to represent class members, such as attributes and methods, and additional information about them.
|
||||||
|
|
||||||
Mermaid distinguishes between attributes and functions/methods based on if the **parenthesis** `()` are present or not. The ones with `()` are treated as functions/methods, and others as attributes. To indicate a return type for a method, enclose the type within **square brackets** `[]`
|
Mermaid distinguishes between attributes and functions/methods based on if the **parenthesis** `()` are present or not. The ones with `()` are treated as functions/methods, and others as attributes.
|
||||||
|
|
||||||
|
|
||||||
There are two ways to define the members of a class, and regardless of whichever syntax is used to define the members, the output will still be same. The two different ways are :
|
There are two ways to define the members of a class, and regardless of whichever syntax is used to define the members, the output will still be same. The two different ways are :
|
||||||
@@ -115,7 +115,7 @@ There are two ways to define the members of a class, and regardless of whichever
|
|||||||
class BankAccount
|
class BankAccount
|
||||||
BankAccount : +String owner
|
BankAccount : +String owner
|
||||||
BankAccount : +BigDecimal balance
|
BankAccount : +BigDecimal balance
|
||||||
BankAccount : +deposit(amount) bool
|
BankAccount : +deposit(amount)
|
||||||
BankAccount : +withdrawal(amount)
|
BankAccount : +withdrawal(amount)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ There are two ways to define the members of a class, and regardless of whichever
|
|||||||
class BankAccount
|
class BankAccount
|
||||||
BankAccount : +String owner
|
BankAccount : +String owner
|
||||||
BankAccount : +BigDecimal balance
|
BankAccount : +BigDecimal balance
|
||||||
BankAccount : +deposit(amount) : bool
|
BankAccount : +deposit(amount)
|
||||||
BankAccount : +withdrawl(amount)
|
BankAccount : +withdrawl(amount)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -142,11 +142,64 @@ class BankAccount{
|
|||||||
class BankAccount{
|
class BankAccount{
|
||||||
+String owner
|
+String owner
|
||||||
+BigDecimal balance
|
+BigDecimal balance
|
||||||
+deposit(amount) : bool
|
+deposit(amount) bool
|
||||||
+withdrawl(amount)
|
+withdrawl(amount) int
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Return Type
|
||||||
|
Optionally you can end the method/function definition with the data type that will be returned (note: there must be a space between the final `)` of the method definition and return type
|
||||||
|
example:
|
||||||
|
```
|
||||||
|
class BankAccount{
|
||||||
|
+String owner
|
||||||
|
+BigDecimal balance
|
||||||
|
+deposit(amount) bool
|
||||||
|
+withdrawl(amount) int
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class BankAccount{
|
||||||
|
+String owner
|
||||||
|
+BigDecimal balance
|
||||||
|
+deposit(amount) bool
|
||||||
|
+withdrawl(amount) int
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Generic Types
|
||||||
|
Members can be defined using generic types, such as `List<int>`, for fields, parameters and return types by enclosing the type within `~` (**tilde**). Note: **nested** type declarations (such as `List<List<int>>`) are not currently supported
|
||||||
|
|
||||||
|
This can be done as part of either class definition method:
|
||||||
|
|
||||||
|
```
|
||||||
|
classDiagram
|
||||||
|
class Square~Shape~{
|
||||||
|
int id
|
||||||
|
List~int~ position
|
||||||
|
setPoints(List~int~ points)
|
||||||
|
getPoints() List~int~
|
||||||
|
}
|
||||||
|
|
||||||
|
Square : -List~string~ messages
|
||||||
|
Square : +setMessages(List~string~ messages)
|
||||||
|
Square : +getMessages() List~string~
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class Square~Shape~{
|
||||||
|
int id
|
||||||
|
List~int~ position
|
||||||
|
setPoints(List~int~ points)
|
||||||
|
getPoints() List~int~
|
||||||
|
}
|
||||||
|
|
||||||
|
Square : -List~string~ messages
|
||||||
|
Square : +setMessages(List~string~ messages)
|
||||||
|
Square : +getMessages() List~string~
|
||||||
|
```
|
||||||
|
|
||||||
#### Return Type
|
#### Return Type
|
||||||
Optionally you can end the method/function definition with the data type that will be returned
|
Optionally you can end the method/function definition with the data type that will be returned
|
||||||
@@ -157,7 +210,7 @@ To specify the visibility of a class member (i.e. any attribute or method), thes
|
|||||||
- `+` Public
|
- `+` Public
|
||||||
- `-` Private
|
- `-` Private
|
||||||
- `#` Protected
|
- `#` Protected
|
||||||
- `~` Package
|
- `~` Package/Internal
|
||||||
|
|
||||||
>_note_ you can also include additional _classifers_ to a method definition by adding the following notations to the end of the method, i.e.: after the `()`:
|
>_note_ you can also include additional _classifers_ to a method definition by adding the following notations to the end of the method, i.e.: after the `()`:
|
||||||
> - `*` Abstract e.g.: `someAbstractMethod()*`
|
> - `*` Abstract e.g.: `someAbstractMethod()*`
|
||||||
|
@@ -380,6 +380,7 @@ describe('class diagram, ', function () {
|
|||||||
|
|
||||||
parser.parse(str);
|
parser.parse(str);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle dashed relation definition of different types and directions', function () {
|
it('should handle dashed relation definition of different types and directions', function () {
|
||||||
const str =
|
const str =
|
||||||
'classDiagram\n' +
|
'classDiagram\n' +
|
||||||
@@ -390,6 +391,29 @@ describe('class diagram, ', function () {
|
|||||||
'Class19 .. Class20';
|
'Class19 .. Class20';
|
||||||
parser.parse(str);
|
parser.parse(str);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle generic types in members', function () {
|
||||||
|
const str =
|
||||||
|
'classDiagram\n' +
|
||||||
|
'class Car~T~\n' +
|
||||||
|
'Car : -List~Wheel~ wheels\n' +
|
||||||
|
'Car : +setWheels(List~Wheel~ wheels)\n' +
|
||||||
|
'Car : +getWheels() List~Wheel~';
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle generic types in members in class with brackets', function () {
|
||||||
|
const str =
|
||||||
|
'classDiagram\n' +
|
||||||
|
'class Car {\n' +
|
||||||
|
'List~Wheel~ wheels\n' +
|
||||||
|
'setWheels(List~Wheel~ wheels)\n' +
|
||||||
|
'+getWheels() List~Wheel~\n' +
|
||||||
|
'}';
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when fetching data from a classDiagram graph it', function () {
|
describe('when fetching data from a classDiagram graph it', function () {
|
||||||
@@ -614,6 +638,7 @@ describe('class diagram, ', function () {
|
|||||||
expect(testClass.cssClasses.length).toBe(1);
|
expect(testClass.cssClasses.length).toBe(1);
|
||||||
expect(testClass.cssClasses[0]).toBe('clickable');
|
expect(testClass.cssClasses[0]).toBe('clickable');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should associate link with tooltip', function () {
|
it('should associate link with tooltip', function () {
|
||||||
const str = 'classDiagram\n' + 'class Class1\n' + 'Class1 : someMethod()\n' + 'link Class1 "google.com" "A tooltip"';
|
const str = 'classDiagram\n' + 'class Class1\n' + 'Class1 : someMethod()\n' + 'link Class1 "google.com" "A tooltip"';
|
||||||
parser.parse(str);
|
parser.parse(str);
|
||||||
|
137
src/diagrams/class/classMemberRenderer.js
Normal file
137
src/diagrams/class/classMemberRenderer.js
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
export const addTspan = function(textEl, txt, isFirst, conf) {
|
||||||
|
let member = parseMember(txt);
|
||||||
|
|
||||||
|
const tSpan = textEl
|
||||||
|
.append('tspan')
|
||||||
|
.attr('x', conf.padding)
|
||||||
|
.text(member.displayText);
|
||||||
|
|
||||||
|
if (member.cssStyle !== '') {
|
||||||
|
tSpan.attr('style', member.cssStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFirst) {
|
||||||
|
tSpan.attr('dy', conf.textHeight);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildFieldDisplay = function(parsedText) {
|
||||||
|
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||||
|
let fieldType = parsedText[2] ? parsedText[2].trim() : '';
|
||||||
|
let genericType = parsedText[3] ? parseGenericTypes(parsedText[3]) : '';
|
||||||
|
let fieldName = parsedText[4] ? parsedText[4].trim() : '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
displayText: visibility + fieldType + genericType + ' ' + fieldName,
|
||||||
|
cssStyle: ''
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildMethodDisplay = function(parsedText) {
|
||||||
|
let cssStyle = '';
|
||||||
|
let displayText = parsedText;
|
||||||
|
|
||||||
|
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||||
|
let methodName = parsedText[2] ? parsedText[2].trim() : '';
|
||||||
|
let parameters = parsedText[3] ? parseGenericTypes(parsedText[3]) : '';
|
||||||
|
let classifier = parsedText[6] ? parsedText[6].trim() : '';
|
||||||
|
let returnType = parsedText[7] ? ' : ' + parseGenericTypes(parsedText[7]).trim() : '';
|
||||||
|
|
||||||
|
displayText = visibility + methodName + '(' + parameters + ')' + returnType;
|
||||||
|
|
||||||
|
cssStyle = parseClassifier(classifier);
|
||||||
|
|
||||||
|
let member = {
|
||||||
|
displayText: displayText,
|
||||||
|
cssStyle: cssStyle
|
||||||
|
};
|
||||||
|
|
||||||
|
return member;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildLegacyDisplay = function(text) {
|
||||||
|
// if for some reason we dont have any match, use old format to parse text
|
||||||
|
let memberText = '';
|
||||||
|
let cssStyle = '';
|
||||||
|
let returnType = '';
|
||||||
|
let methodStart = text.indexOf('(');
|
||||||
|
let methodEnd = text.indexOf(')');
|
||||||
|
|
||||||
|
if (methodStart > 1 && methodEnd > methodStart && methodEnd <= text.length) {
|
||||||
|
let parsedText = text.match(/(\+|-|~|#)?(\w+)/);
|
||||||
|
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||||
|
let methodName = parsedText[2];
|
||||||
|
let parameters = text.substring(methodStart + 1, methodEnd);
|
||||||
|
let classifier = text.substring(methodEnd, methodEnd + 1);
|
||||||
|
cssStyle = parseClassifier(classifier);
|
||||||
|
|
||||||
|
memberText = visibility + methodName + '(' + parseGenericTypes(parameters.trim()) + ')';
|
||||||
|
|
||||||
|
if (methodEnd < memberText.length) {
|
||||||
|
returnType = text.substring(methodEnd + 2).trim();
|
||||||
|
if (returnType !== '') {
|
||||||
|
returnType = ' : ' + parseGenericTypes(returnType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// finally - if all else fails, just send the text back as written (other than parsing for generic types)
|
||||||
|
memberText = parseGenericTypes(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
let member = {
|
||||||
|
displayText: memberText + returnType,
|
||||||
|
cssStyle: cssStyle
|
||||||
|
};
|
||||||
|
|
||||||
|
return member;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseGenericTypes = function(text) {
|
||||||
|
let cleanedText = text;
|
||||||
|
|
||||||
|
if (text.indexOf('~') != -1) {
|
||||||
|
cleanedText = cleanedText.replace('~', '<');
|
||||||
|
cleanedText = cleanedText.replace('~', '>');
|
||||||
|
|
||||||
|
return parseGenericTypes(cleanedText);
|
||||||
|
} else {
|
||||||
|
return cleanedText;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseMember = function(text) {
|
||||||
|
const fieldRegEx = /^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+)$/;
|
||||||
|
const methodRegEx = /^(\+|-|~|#)?(\w+)\s?\(\s*(\w+(~\w+~|\[\])?\s*(\w+)?)?\s*\)\s?([*|$])?\s?(\w+(~\w+~|\[\])?)?\s*$/;
|
||||||
|
//const methodRegEx = /(\+|-|~|#)?(\w+)\s?\(\s*(\w+(~\w+~|\[\])?\s*(\w+)?)?\s*\)\s?([*|$])?\s?(\w+(~\w+~|\[\])?)?/;
|
||||||
|
|
||||||
|
let fieldMatch = text.match(fieldRegEx);
|
||||||
|
let methodMatch = text.match(methodRegEx);
|
||||||
|
|
||||||
|
if (fieldMatch) {
|
||||||
|
return buildFieldDisplay(fieldMatch);
|
||||||
|
} else if (methodMatch) {
|
||||||
|
return buildMethodDisplay(methodMatch);
|
||||||
|
} else {
|
||||||
|
return buildLegacyDisplay(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseClassifier = function(classifier) {
|
||||||
|
switch (classifier) {
|
||||||
|
case '*':
|
||||||
|
return 'font-style:italic;';
|
||||||
|
case '$':
|
||||||
|
return 'text-decoration:underline;';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
addTspan,
|
||||||
|
buildFieldDisplay,
|
||||||
|
buildLegacyDisplay,
|
||||||
|
buildMethodDisplay,
|
||||||
|
parseGenericTypes,
|
||||||
|
parseMember
|
||||||
|
};
|
168
src/diagrams/class/classMemberRenderer.spec.js
Normal file
168
src/diagrams/class/classMemberRenderer.spec.js
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/* eslint-env jasmine */
|
||||||
|
import memberRenderer from './classMemberRenderer';
|
||||||
|
|
||||||
|
describe('class member Renderer, ', function () {
|
||||||
|
describe('when parsing text to build method display string', function () {
|
||||||
|
it('should handle simple method declaration', function () {
|
||||||
|
const str = 'foo()';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo()');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle public visibility', function () {
|
||||||
|
const str = '+foo()';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('+foo()');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle private visibility', function () {
|
||||||
|
const str = '-foo()';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('-foo()');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle protected visibility', function () {
|
||||||
|
const str = '#foo()';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('#foo()');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle package/internal visibility', function () {
|
||||||
|
const str = '~foo()';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('~foo()');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore unknown character for visibility', function () {
|
||||||
|
const str = '!foo()';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo()');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle abstract classifier', function () {
|
||||||
|
const str = 'foo()*';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo()');
|
||||||
|
expect(actual.cssStyle).toBe('font-style:italic;');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle static classifier', function () {
|
||||||
|
const str = 'foo()$';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo()');
|
||||||
|
expect(actual.cssStyle).toBe('text-decoration:underline;');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore unknown character for classifier', function () {
|
||||||
|
const str = 'foo()!';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo()');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle simple method declaration with parameters', function () {
|
||||||
|
const str = 'foo(int id)';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo(int id)');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle simple method declaration with single item in parameters', function () {
|
||||||
|
const str = 'foo(id)';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo(id)');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle simple method declaration with single item in parameters with extra spaces', function () {
|
||||||
|
const str = ' foo ( id) ';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo(id)');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle method declaration with return value', function () {
|
||||||
|
const str = 'foo(id) int';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo(id) : int');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle method declaration with generic return value', function () {
|
||||||
|
const str = 'foo(id) List~int~';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo(id) : List<int>');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle method declaration with generic parameter', function () {
|
||||||
|
const str = 'foo(List~int~)';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('foo(List<int>)');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle method declaration with all possible markup', function () {
|
||||||
|
const str = '+foo ( List~int~ ids )* List~Item~';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('+foo(List<int> ids) : List<Item>');
|
||||||
|
expect(actual.cssStyle).toBe('font-style:italic;');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when parsing text for generic types', function () {
|
||||||
|
it('should handle open and close brackets in correct order', function () {
|
||||||
|
const str = 'foo(List~Item~)';
|
||||||
|
let actual = memberRenderer.parseGenericTypes(str);
|
||||||
|
|
||||||
|
expect(actual).toBe('foo(List<Item>)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle open and close brackets in correct order with multiple usages', function () {
|
||||||
|
const str = 'foo(List~Item~) List~Item~';
|
||||||
|
let actual = memberRenderer.parseGenericTypes(str);
|
||||||
|
|
||||||
|
expect(actual).toBe('foo(List<Item>) List<Item>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when parsing text to build field display string', function () {
|
||||||
|
it('should handle simple field declaration', function () {
|
||||||
|
const str = 'int[] ids';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('int[] ids');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle field declaration with generic type', function () {
|
||||||
|
const str = 'List~int~ ids';
|
||||||
|
let actual = memberRenderer.parseMember(str);
|
||||||
|
|
||||||
|
expect(actual.displayText).toBe('List<int> ids');
|
||||||
|
expect(actual.cssStyle).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -5,6 +5,7 @@ import { logger } from '../../logger';
|
|||||||
import classDb, { lookUpDomId } from './classDb';
|
import classDb, { lookUpDomId } from './classDb';
|
||||||
import utils from '../../utils';
|
import utils from '../../utils';
|
||||||
import { parser } from './parser/classDiagram';
|
import { parser } from './parser/classDiagram';
|
||||||
|
import memberRenderer from './classMemberRenderer';
|
||||||
|
|
||||||
parser.yy = classDb;
|
parser.yy = classDb;
|
||||||
|
|
||||||
@@ -287,75 +288,6 @@ const drawClass = function(elem, classDef) {
|
|||||||
cssClassStr = cssClassStr + classDef.cssClasses.join(' ');
|
cssClassStr = cssClassStr + classDef.cssClasses.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
const addTspan = function(textEl, txt, isFirst) {
|
|
||||||
let isMethod = txt.indexOf(')') > 1;
|
|
||||||
let displayText = txt;
|
|
||||||
let cssStyle = '';
|
|
||||||
|
|
||||||
if (isMethod) {
|
|
||||||
let method = buildDisplayTextForMethod(txt);
|
|
||||||
displayText = method.displayText;
|
|
||||||
cssStyle = method.cssStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tSpan = textEl
|
|
||||||
.append('tspan')
|
|
||||||
.attr('x', conf.padding)
|
|
||||||
.text(displayText);
|
|
||||||
|
|
||||||
if (cssStyle !== '') {
|
|
||||||
tSpan.attr('style', cssStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isFirst) {
|
|
||||||
tSpan.attr('dy', conf.textHeight);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const buildDisplayTextForMethod = function(txt) {
|
|
||||||
let regEx = /(\+|-|~|#)?(\w+)\s?\((\w+(<\w+>|\[\])?\s?(\w+)?)?\)\s?([*|$])?\s?(\w+(<\w+>|\[\])?)?/;
|
|
||||||
|
|
||||||
let cssStyle = '';
|
|
||||||
let displayText = txt;
|
|
||||||
let methodName = txt;
|
|
||||||
let classifier = '';
|
|
||||||
|
|
||||||
let parsedText = txt.match(regEx);
|
|
||||||
|
|
||||||
if (parsedText) {
|
|
||||||
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
|
||||||
methodName = parsedText[2] ? parsedText[2].trim() : '';
|
|
||||||
let parameters = parsedText[3] ? parsedText[3].trim() : '';
|
|
||||||
classifier = parsedText[6] ? parsedText[6].trim() : '';
|
|
||||||
let returnType = parsedText[7] ? ' : ' + parsedText[7].trim() : '';
|
|
||||||
|
|
||||||
displayText = visibility + methodName + '(' + parameters + ')' + returnType;
|
|
||||||
} else {
|
|
||||||
let methodEnd = displayText.indexOf(')') + 1;
|
|
||||||
classifier = displayText.substring(methodEnd, methodEnd + 1);
|
|
||||||
if (classifier !== '' && classifier !== ' ') {
|
|
||||||
displayText = displayText.replace(classifier, '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (classifier) {
|
|
||||||
case '*':
|
|
||||||
cssStyle = 'font-style:italic;';
|
|
||||||
break;
|
|
||||||
case '$':
|
|
||||||
cssStyle = 'text-decoration:underline;';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let method = {
|
|
||||||
methodname: methodName,
|
|
||||||
displayText: displayText,
|
|
||||||
cssStyle: cssStyle
|
|
||||||
};
|
|
||||||
|
|
||||||
return method;
|
|
||||||
};
|
|
||||||
|
|
||||||
const id = classDef.id;
|
const id = classDef.id;
|
||||||
const classInfo = {
|
const classInfo = {
|
||||||
id: id,
|
id: id,
|
||||||
@@ -426,7 +358,7 @@ const drawClass = function(elem, classDef) {
|
|||||||
|
|
||||||
isFirst = true;
|
isFirst = true;
|
||||||
classDef.members.forEach(function(member) {
|
classDef.members.forEach(function(member) {
|
||||||
addTspan(members, member, isFirst);
|
memberRenderer.addTspan(members, member, isFirst, conf);
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -448,7 +380,7 @@ const drawClass = function(elem, classDef) {
|
|||||||
isFirst = true;
|
isFirst = true;
|
||||||
|
|
||||||
classDef.methods.forEach(function(method) {
|
classDef.methods.forEach(function(method) {
|
||||||
addTspan(methods, method, isFirst);
|
memberRenderer.addTspan(methods, method, isFirst, conf);
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user