Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
f4789c5c
Commit
f4789c5c
authored
May 04, 2020
by
Himanshu Kapoor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Web IDE: Add support for .vue files
Add support for syntax highlighting .vue files in the Web IDE.
parent
52952037
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
541 additions
and
12 deletions
+541
-12
app/assets/javascripts/editor/editor_lite.js
app/assets/javascripts/editor/editor_lite.js
+4
-0
app/assets/javascripts/ide/lib/editor.js
app/assets/javascripts/ide/lib/editor.js
+3
-0
app/assets/javascripts/ide/lib/languages/index.js
app/assets/javascripts/ide/lib/languages/index.js
+5
-0
app/assets/javascripts/ide/lib/languages/vue.js
app/assets/javascripts/ide/lib/languages/vue.js
+306
-0
app/assets/javascripts/ide/utils.js
app/assets/javascripts/ide/utils.js
+10
-0
changelogs/unreleased/212279-webide-vue-files.yml
changelogs/unreleased/212279-webide-vue-files.yml
+5
-0
spec/frontend/editor/editor_lite_spec.js
spec/frontend/editor/editor_lite_spec.js
+27
-10
spec/frontend/ide/lib/editor_spec.js
spec/frontend/ide/lib/editor_spec.js
+13
-1
spec/frontend/ide/lib/languages/vue_spec.js
spec/frontend/ide/lib/languages/vue_spec.js
+92
-0
spec/frontend/ide/utils_spec.js
spec/frontend/ide/utils_spec.js
+76
-1
No files found.
app/assets/javascripts/editor/editor_lite.js
View file @
f4789c5c
import
{
editor
as
monacoEditor
,
languages
as
monacoLanguages
,
Uri
}
from
'
monaco-editor
'
;
import
{
DEFAULT_THEME
,
themes
}
from
'
~/ide/lib/themes
'
;
import
languages
from
'
~/ide/lib/languages
'
;
import
{
defaultEditorOptions
}
from
'
~/ide/lib/editor_options
'
;
import
{
registerLanguages
}
from
'
~/ide/utils
'
;
import
{
clearDomElement
}
from
'
./utils
'
;
export
default
class
Editor
{
...
...
@@ -17,6 +19,8 @@ export default class Editor {
};
Editor
.
setupMonacoTheme
();
registerLanguages
(...
languages
);
}
static
setupMonacoTheme
()
{
...
...
app/assets/javascripts/ide/lib/editor.js
View file @
f4789c5c
...
...
@@ -7,8 +7,10 @@ import Disposable from './common/disposable';
import
ModelManager
from
'
./common/model_manager
'
;
import
editorOptions
,
{
defaultEditorOptions
}
from
'
./editor_options
'
;
import
{
themes
}
from
'
./themes
'
;
import
languages
from
'
./languages
'
;
import
keymap
from
'
./keymap.json
'
;
import
{
clearDomElement
}
from
'
~/editor/utils
'
;
import
{
registerLanguages
}
from
'
../utils
'
;
function
setupThemes
()
{
themes
.
forEach
(
theme
=>
{
...
...
@@ -37,6 +39,7 @@ export default class Editor {
};
setupThemes
();
registerLanguages
(...
languages
);
this
.
debouncedUpdate
=
debounce
(()
=>
{
this
.
updateDimensions
();
...
...
app/assets/javascripts/ide/lib/languages/index.js
0 → 100644
View file @
f4789c5c
import
vue
from
'
./vue
'
;
const
languages
=
[
vue
];
export
default
languages
;
app/assets/javascripts/ide/lib/languages/vue.js
0 → 100644
View file @
f4789c5c
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See https://github.com/microsoft/monaco-languages/blob/master/LICENSE.md
*--------------------------------------------------------------------------------------------*/
// Based on handlebars template in https://github.com/microsoft/monaco-languages/blob/master/src/handlebars/handlebars.ts
// Look for "vuejs template attributes" in this file for Vue specific syntax.
import
{
languages
}
from
'
monaco-editor
'
;
/* eslint-disable no-useless-escape */
/* eslint-disable @gitlab/require-i18n-strings */
const
EMPTY_ELEMENTS
=
[
'
area
'
,
'
base
'
,
'
br
'
,
'
col
'
,
'
embed
'
,
'
hr
'
,
'
img
'
,
'
input
'
,
'
keygen
'
,
'
link
'
,
'
menuitem
'
,
'
meta
'
,
'
param
'
,
'
source
'
,
'
track
'
,
'
wbr
'
,
];
const
conf
=
{
wordPattern
:
/
(
-
?\d
*
\.\d\w
*
)
|
([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]
+
)
/g
,
comments
:
{
blockComment
:
[
'
{{!--
'
,
'
--}}
'
],
},
brackets
:
[[
'
<!--
'
,
'
-->
'
],
[
'
<
'
,
'
>
'
],
[
'
{{
'
,
'
}}
'
],
[
'
{
'
,
'
}
'
],
[
'
(
'
,
'
)
'
]],
autoClosingPairs
:
[
{
open
:
'
{
'
,
close
:
'
}
'
},
{
open
:
'
[
'
,
close
:
'
]
'
},
{
open
:
'
(
'
,
close
:
'
)
'
},
{
open
:
'
"
'
,
close
:
'
"
'
},
{
open
:
"
'
"
,
close
:
"
'
"
},
],
surroundingPairs
:
[
{
open
:
'
<
'
,
close
:
'
>
'
},
{
open
:
'
"
'
,
close
:
'
"
'
},
{
open
:
"
'
"
,
close
:
"
'
"
},
],
onEnterRules
:
[
{
beforeText
:
new
RegExp
(
`<(?!(?:
${
EMPTY_ELEMENTS
.
join
(
'
|
'
)}
))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`
,
'
i
'
,
),
afterText
:
/^<
\/(\w[\w\d]
*
)\s
*>$/i
,
action
:
{
indentAction
:
languages
.
IndentAction
.
IndentOutdent
},
},
{
beforeText
:
new
RegExp
(
`<(?!(?:
${
EMPTY_ELEMENTS
.
join
(
'
|
'
)}
))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`
,
'
i
'
,
),
action
:
{
indentAction
:
languages
.
IndentAction
.
Indent
},
},
],
};
const
language
=
{
defaultToken
:
''
,
tokenPostfix
:
''
,
// ignoreCase: true,
// The main tokenizer for our languages
tokenizer
:
{
root
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.root
'
}],
[
/<!DOCTYPE/
,
'
metatag.html
'
,
'
@doctype
'
],
[
/<!--/
,
'
comment.html
'
,
'
@comment
'
],
[
/
(
<
)([\w]
+
)(\/
>
)
/
,
[
'
delimiter.html
'
,
'
tag.html
'
,
'
delimiter.html
'
]],
[
/
(
<
)(
script
)
/
,
[
'
delimiter.html
'
,
{
token
:
'
tag.html
'
,
next
:
'
@script
'
}]],
[
/
(
<
)(
style
)
/
,
[
'
delimiter.html
'
,
{
token
:
'
tag.html
'
,
next
:
'
@style
'
}]],
[
/
(
<
)([
:
\w]
+
)
/
,
[
'
delimiter.html
'
,
{
token
:
'
tag.html
'
,
next
:
'
@otherTag
'
}]],
[
/
(
<
\/)([\w]
+
)
/
,
[
'
delimiter.html
'
,
{
token
:
'
tag.html
'
,
next
:
'
@otherTag
'
}]],
[
/</
,
'
delimiter.html
'
],
[
/
\{
/
,
'
delimiter.html
'
],
[
/
[^
<{
]
+/
],
// text
],
doctype
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.comment
'
}],
[
/
[^
>
]
+/
,
'
metatag.content.html
'
],
[
/>/
,
'
metatag.html
'
,
'
@pop
'
],
],
comment
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.comment
'
}],
[
/-->/
,
'
comment.html
'
,
'
@pop
'
],
[
/
[^
-
]
+/
,
'
comment.content.html
'
],
[
/./
,
'
comment.content.html
'
],
],
otherTag
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.otherTag
'
}],
[
/
\/?
>/
,
'
delimiter.html
'
,
'
@pop
'
],
// -- BEGIN vuejs template attributes
[
/
(
v-|@|:
)[\w\-\.\:\[\]]
+="
([^
"
]
*
)
"/
,
'
variable
'
],
[
/
(
v-|@|:
)[\w\-\.\:\[\]]
+='
([^
'
]
*
)
'/
,
'
variable
'
],
[
/"
([^
"
]
*
)
"/
,
'
attribute.value
'
],
[
/'
([^
'
]
*
)
'/
,
'
attribute.value
'
],
[
/
[\w\-\.\:\[\]]
+/
,
'
attribute.name
'
],
// -- END vuejs template attributes
[
/=/
,
'
delimiter
'
],
[
/
[
\t\r\n]
+/
],
// whitespace
],
// -- BEGIN <script> tags handling
// After <script
script
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.script
'
}],
[
/type/
,
'
attribute.name
'
,
'
@scriptAfterType
'
],
[
/"
([^
"
]
*
)
"/
,
'
attribute.value
'
],
[
/'
([^
'
]
*
)
'/
,
'
attribute.value
'
],
[
/
[\w\-]
+/
,
'
attribute.name
'
],
[
/=/
,
'
delimiter
'
],
[
/>/
,
{
token
:
'
delimiter.html
'
,
next
:
'
@scriptEmbedded.text/javascript
'
,
nextEmbedded
:
'
text/javascript
'
,
},
],
[
/
[
\t\r\n]
+/
],
// whitespace
[
/
(
<
\/)(
script
\s
*
)(
>
)
/
,
[
'
delimiter.html
'
,
'
tag.html
'
,
{
token
:
'
delimiter.html
'
,
next
:
'
@pop
'
}],
],
],
// After <script ... type
scriptAfterType
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.scriptAfterType
'
}],
[
/=/
,
'
delimiter
'
,
'
@scriptAfterTypeEquals
'
],
[
/>/
,
{
token
:
'
delimiter.html
'
,
next
:
'
@scriptEmbedded.text/javascript
'
,
nextEmbedded
:
'
text/javascript
'
,
},
],
// cover invalid e.g. <script type>
[
/
[
\t\r\n]
+/
],
// whitespace
[
/<
\/
script
\s
*>/
,
{
token
:
'
@rematch
'
,
next
:
'
@pop
'
}],
],
// After <script ... type =
scriptAfterTypeEquals
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.scriptAfterTypeEquals
'
}],
[
/"
([^
"
]
*
)
"/
,
{
token
:
'
attribute.value
'
,
switchTo
:
'
@scriptWithCustomType.$1
'
}],
[
/'
([^
'
]
*
)
'/
,
{
token
:
'
attribute.value
'
,
switchTo
:
'
@scriptWithCustomType.$1
'
}],
[
/>/
,
{
token
:
'
delimiter.html
'
,
next
:
'
@scriptEmbedded.text/javascript
'
,
nextEmbedded
:
'
text/javascript
'
,
},
],
// cover invalid e.g. <script type=>
[
/
[
\t\r\n]
+/
],
// whitespace
[
/<
\/
script
\s
*>/
,
{
token
:
'
@rematch
'
,
next
:
'
@pop
'
}],
],
// After <script ... type = $S2
scriptWithCustomType
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.scriptWithCustomType.$S2
'
},
],
[
/>/
,
{
token
:
'
delimiter.html
'
,
next
:
'
@scriptEmbedded.$S2
'
,
nextEmbedded
:
'
$S2
'
}],
[
/"
([^
"
]
*
)
"/
,
'
attribute.value
'
],
[
/'
([^
'
]
*
)
'/
,
'
attribute.value
'
],
[
/
[\w\-]
+/
,
'
attribute.name
'
],
[
/=/
,
'
delimiter
'
],
[
/
[
\t\r\n]
+/
],
// whitespace
[
/<
\/
script
\s
*>/
,
{
token
:
'
@rematch
'
,
next
:
'
@pop
'
}],
],
scriptEmbedded
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInEmbeddedState.scriptEmbedded.$S2
'
,
nextEmbedded
:
'
@pop
'
,
},
],
[
/<
\/
script/
,
{
token
:
'
@rematch
'
,
next
:
'
@pop
'
,
nextEmbedded
:
'
@pop
'
}],
],
// -- END <script> tags handling
// -- BEGIN <style> tags handling
// After <style
style
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.style
'
}],
[
/type/
,
'
attribute.name
'
,
'
@styleAfterType
'
],
[
/"
([^
"
]
*
)
"/
,
'
attribute.value
'
],
[
/'
([^
'
]
*
)
'/
,
'
attribute.value
'
],
[
/
[\w\-]
+/
,
'
attribute.name
'
],
[
/=/
,
'
delimiter
'
],
[
/>/
,
{
token
:
'
delimiter.html
'
,
next
:
'
@styleEmbedded.text/css
'
,
nextEmbedded
:
'
text/css
'
}],
[
/
[
\t\r\n]
+/
],
// whitespace
[
/
(
<
\/)(
style
\s
*
)(
>
)
/
,
[
'
delimiter.html
'
,
'
tag.html
'
,
{
token
:
'
delimiter.html
'
,
next
:
'
@pop
'
}],
],
],
// After <style ... type
styleAfterType
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.styleAfterType
'
}],
[
/=/
,
'
delimiter
'
,
'
@styleAfterTypeEquals
'
],
[
/>/
,
{
token
:
'
delimiter.html
'
,
next
:
'
@styleEmbedded.text/css
'
,
nextEmbedded
:
'
text/css
'
}],
// cover invalid e.g. <style type>
[
/
[
\t\r\n]
+/
],
// whitespace
[
/<
\/
style
\s
*>/
,
{
token
:
'
@rematch
'
,
next
:
'
@pop
'
}],
],
// After <style ... type =
styleAfterTypeEquals
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.styleAfterTypeEquals
'
}],
[
/"
([^
"
]
*
)
"/
,
{
token
:
'
attribute.value
'
,
switchTo
:
'
@styleWithCustomType.$1
'
}],
[
/'
([^
'
]
*
)
'/
,
{
token
:
'
attribute.value
'
,
switchTo
:
'
@styleWithCustomType.$1
'
}],
[
/>/
,
{
token
:
'
delimiter.html
'
,
next
:
'
@styleEmbedded.text/css
'
,
nextEmbedded
:
'
text/css
'
}],
// cover invalid e.g. <style type=>
[
/
[
\t\r\n]
+/
],
// whitespace
[
/<
\/
style
\s
*>/
,
{
token
:
'
@rematch
'
,
next
:
'
@pop
'
}],
],
// After <style ... type = $S2
styleWithCustomType
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInSimpleState.styleWithCustomType.$S2
'
}],
[
/>/
,
{
token
:
'
delimiter.html
'
,
next
:
'
@styleEmbedded.$S2
'
,
nextEmbedded
:
'
$S2
'
}],
[
/"
([^
"
]
*
)
"/
,
'
attribute.value
'
],
[
/'
([^
'
]
*
)
'/
,
'
attribute.value
'
],
[
/
[\w\-]
+/
,
'
attribute.name
'
],
[
/=/
,
'
delimiter
'
],
[
/
[
\t\r\n]
+/
],
// whitespace
[
/<
\/
style
\s
*>/
,
{
token
:
'
@rematch
'
,
next
:
'
@pop
'
}],
],
styleEmbedded
:
[
[
/
\{\{
/
,
{
token
:
'
@rematch
'
,
switchTo
:
'
@handlebarsInEmbeddedState.styleEmbedded.$S2
'
,
nextEmbedded
:
'
@pop
'
,
},
],
[
/<
\/
style/
,
{
token
:
'
@rematch
'
,
next
:
'
@pop
'
,
nextEmbedded
:
'
@pop
'
}],
],
// -- END <style> tags handling
handlebarsInSimpleState
:
[
[
/
\{\{\{?
/
,
'
delimiter.handlebars
'
],
[
/
\}\}\}?
/
,
{
token
:
'
delimiter.handlebars
'
,
switchTo
:
'
@$S2.$S3
'
}],
{
include
:
'
handlebarsRoot
'
},
],
handlebarsInEmbeddedState
:
[
[
/
\{\{\{?
/
,
'
delimiter.handlebars
'
],
[
/
\}\}\}?
/
,
{
token
:
'
delimiter.handlebars
'
,
switchTo
:
'
@$S2.$S3
'
,
nextEmbedded
:
'
$S3
'
}],
{
include
:
'
handlebarsRoot
'
},
],
handlebarsRoot
:
[
[
/"
[^
"
]
*"/
,
'
string.handlebars
'
],
[
/
[
#
/][^\s
}
]
+/
,
'
keyword.helper.handlebars
'
],
[
/else
\b
/
,
'
keyword.helper.handlebars
'
],
[
/
[\s]
+/
],
[
/
[^
}
]
/
,
'
variable.parameter.handlebars
'
],
],
},
};
export
default
{
id
:
'
vue
'
,
extensions
:
[
'
.vue
'
],
aliases
:
[
'
Vue
'
,
'
vue
'
],
mimetypes
:
[
'
text/x-vue-template
'
],
conf
,
language
,
};
app/assets/javascripts/ide/utils.js
View file @
f4789c5c
...
...
@@ -68,3 +68,13 @@ export const createPathWithExt = p => {
return
`
${
p
.
substring
(
1
,
p
.
lastIndexOf
(
'
.
'
)
+
1
||
p
.
length
)}${
ext
||
'
.js
'
}
`
;
};
export
function
registerLanguages
(
def
,
...
defs
)
{
if
(
defs
.
length
)
defs
.
forEach
(
lang
=>
registerLanguages
(
lang
));
const
languageId
=
def
.
id
;
languages
.
register
(
def
);
languages
.
setMonarchTokensProvider
(
languageId
,
def
.
language
);
languages
.
setLanguageConfiguration
(
languageId
,
def
.
conf
);
}
changelogs/unreleased/212279-webide-vue-files.yml
0 → 100644
View file @
f4789c5c
---
title
:
'
Web
IDE:
Introduce
syntax
highlighting
for
.vue
files.'
merge_request
:
30986
author
:
type
:
added
spec/
javascripts
/editor/editor_lite_spec.js
→
spec/
frontend
/editor/editor_lite_spec.js
View file @
f4789c5c
import
{
editor
as
monacoEditor
,
Uri
}
from
'
monaco-editor
'
;
import
{
editor
as
monacoEditor
,
languages
as
monacoLanguages
,
Uri
}
from
'
monaco-editor
'
;
import
Editor
from
'
~/editor/editor_lite
'
;
import
{
DEFAULT_THEME
,
themes
}
from
'
~/ide/lib/themes
'
;
...
...
@@ -41,13 +41,13 @@ describe('Base editor', () => {
let
dispose
;
beforeEach
(()
=>
{
setModel
=
j
asmine
.
createSpy
();
dispose
=
j
asmine
.
createSpy
();
modelSpy
=
spyOn
(
monacoEditor
,
'
createModel
'
).
and
.
returnValue
(
fakeModel
);
instanceSpy
=
spyOn
(
monacoEditor
,
'
create
'
).
and
.
returnValue
({
setModel
=
j
est
.
fn
();
dispose
=
j
est
.
fn
();
modelSpy
=
jest
.
spyOn
(
monacoEditor
,
'
createModel
'
).
mockImplementation
(()
=>
fakeModel
);
instanceSpy
=
jest
.
spyOn
(
monacoEditor
,
'
create
'
).
mockImplementation
(()
=>
({
setModel
,
dispose
,
});
})
)
;
});
it
(
'
does nothing if no dom element is supplied
'
,
()
=>
{
...
...
@@ -73,7 +73,7 @@ describe('Base editor', () => {
editor
.
createInstance
({
el
:
editorEl
});
expect
(
editor
.
editorEl
).
not
.
toBe
(
null
);
expect
(
instanceSpy
).
toHaveBeenCalledWith
(
editorEl
,
jasmine
.
anything
());
expect
(
instanceSpy
).
toHaveBeenCalledWith
(
editorEl
,
expect
.
anything
());
});
});
...
...
@@ -91,6 +91,11 @@ describe('Base editor', () => {
});
it
(
'
is capable of changing the language of the model
'
,
()
=>
{
// ignore warnings and errors Monaco posts during setup
// (due to being called from Jest/Node.js environment)
jest
.
spyOn
(
console
,
'
warn
'
).
mockImplementation
(()
=>
{});
jest
.
spyOn
(
console
,
'
error
'
).
mockImplementation
(()
=>
{});
const
blobRenamedPath
=
'
test.js
'
;
expect
(
editor
.
model
.
getLanguageIdentifier
().
language
).
toEqual
(
'
markdown
'
);
...
...
@@ -101,7 +106,7 @@ describe('Base editor', () => {
it
(
'
falls back to plaintext if there is no language associated with an extension
'
,
()
=>
{
const
blobRenamedPath
=
'
test.myext
'
;
const
spy
=
spyOn
(
console
,
'
error
'
);
const
spy
=
jest
.
spyOn
(
console
,
'
error
'
).
mockImplementation
(()
=>
{}
);
editor
.
updateModelLanguage
(
blobRenamedPath
);
...
...
@@ -110,14 +115,26 @@ describe('Base editor', () => {
});
});
describe
(
'
languages
'
,
()
=>
{
it
(
'
registers custom languages defined with Monaco
'
,
()
=>
{
expect
(
monacoLanguages
.
getLanguages
()).
toEqual
(
expect
.
arrayContaining
([
expect
.
objectContaining
({
id
:
'
vue
'
,
}),
]),
);
});
});
describe
(
'
syntax highlighting theme
'
,
()
=>
{
let
themeDefineSpy
;
let
themeSetSpy
;
let
defaultScheme
;
beforeEach
(()
=>
{
themeDefineSpy
=
spyOn
(
monacoEditor
,
'
defineTheme
'
);
themeSetSpy
=
spyOn
(
monacoEditor
,
'
setTheme
'
);
themeDefineSpy
=
jest
.
spyOn
(
monacoEditor
,
'
defineTheme
'
).
mockImplementation
(()
=>
{}
);
themeSetSpy
=
jest
.
spyOn
(
monacoEditor
,
'
setTheme
'
).
mockImplementation
(()
=>
{}
);
defaultScheme
=
window
.
gon
.
user_color_scheme
;
});
...
...
spec/frontend/ide/lib/editor_spec.js
View file @
f4789c5c
import
{
editor
as
monacoEditor
}
from
'
monaco-editor
'
;
import
{
editor
as
monacoEditor
,
languages
as
monacoLanguages
}
from
'
monaco-editor
'
;
import
Editor
from
'
~/ide/lib/editor
'
;
import
{
defaultEditorOptions
}
from
'
~/ide/lib/editor_options
'
;
import
{
file
}
from
'
../helpers
'
;
...
...
@@ -181,6 +181,18 @@ describe('Multi-file editor library', () => {
});
});
describe
(
'
languages
'
,
()
=>
{
it
(
'
registers custom languages defined with Monaco
'
,
()
=>
{
expect
(
monacoLanguages
.
getLanguages
()).
toEqual
(
expect
.
arrayContaining
([
expect
.
objectContaining
({
id
:
'
vue
'
,
}),
]),
);
});
});
describe
(
'
dispose
'
,
()
=>
{
it
(
'
calls disposble dispose method
'
,
()
=>
{
jest
.
spyOn
(
instance
.
disposable
,
'
dispose
'
);
...
...
spec/frontend/ide/lib/languages/vue_spec.js
0 → 100644
View file @
f4789c5c
import
{
editor
}
from
'
monaco-editor
'
;
import
{
registerLanguages
}
from
'
~/ide/utils
'
;
import
vue
from
'
~/ide/lib/languages/vue
'
;
// This file only tests syntax specific to vue. This does not test existing syntaxes
// of html, javascript, css and handlebars, which vue files extend.
describe
(
'
tokenization for .vue files
'
,
()
=>
{
beforeEach
(()
=>
{
registerLanguages
(
vue
);
});
test
.
each
([
[
'
<div v-if="something">content</div>
'
,
[
[
{
language
:
'
vue
'
,
offset
:
0
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
1
,
type
:
'
tag.html
'
},
{
language
:
'
vue
'
,
offset
:
4
,
type
:
''
},
{
language
:
'
vue
'
,
offset
:
5
,
type
:
'
variable
'
},
{
language
:
'
vue
'
,
offset
:
21
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
22
,
type
:
''
},
{
language
:
'
vue
'
,
offset
:
29
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
31
,
type
:
'
tag.html
'
},
{
language
:
'
vue
'
,
offset
:
34
,
type
:
'
delimiter.html
'
},
],
],
],
[
'
<input :placeholder="placeholder">
'
,
[
[
{
language
:
'
vue
'
,
offset
:
0
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
1
,
type
:
'
tag.html
'
},
{
language
:
'
vue
'
,
offset
:
6
,
type
:
''
},
{
language
:
'
vue
'
,
offset
:
7
,
type
:
'
variable
'
},
{
language
:
'
vue
'
,
offset
:
33
,
type
:
'
delimiter.html
'
},
],
],
],
[
'
<gl-modal @ok="submitForm()"></gl-modal>
'
,
[
[
{
language
:
'
vue
'
,
offset
:
0
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
1
,
type
:
'
tag.html
'
},
{
language
:
'
vue
'
,
offset
:
3
,
type
:
'
attribute.name
'
},
{
language
:
'
vue
'
,
offset
:
9
,
type
:
''
},
{
language
:
'
vue
'
,
offset
:
10
,
type
:
'
variable
'
},
{
language
:
'
vue
'
,
offset
:
28
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
31
,
type
:
'
tag.html
'
},
{
language
:
'
vue
'
,
offset
:
33
,
type
:
'
attribute.name
'
},
{
language
:
'
vue
'
,
offset
:
39
,
type
:
'
delimiter.html
'
},
],
],
],
[
'
<a v-on:click.stop="doSomething">...</a>
'
,
[
[
{
language
:
'
vue
'
,
offset
:
0
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
1
,
type
:
'
tag.html
'
},
{
language
:
'
vue
'
,
offset
:
2
,
type
:
''
},
{
language
:
'
vue
'
,
offset
:
3
,
type
:
'
variable
'
},
{
language
:
'
vue
'
,
offset
:
32
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
33
,
type
:
''
},
{
language
:
'
vue
'
,
offset
:
36
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
38
,
type
:
'
tag.html
'
},
{
language
:
'
vue
'
,
offset
:
39
,
type
:
'
delimiter.html
'
},
],
],
],
[
'
<a @[event]="doSomething">...</a>
'
,
[
[
{
language
:
'
vue
'
,
offset
:
0
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
1
,
type
:
'
tag.html
'
},
{
language
:
'
vue
'
,
offset
:
2
,
type
:
''
},
{
language
:
'
vue
'
,
offset
:
3
,
type
:
'
variable
'
},
{
language
:
'
vue
'
,
offset
:
25
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
26
,
type
:
''
},
{
language
:
'
vue
'
,
offset
:
29
,
type
:
'
delimiter.html
'
},
{
language
:
'
vue
'
,
offset
:
31
,
type
:
'
tag.html
'
},
{
language
:
'
vue
'
,
offset
:
32
,
type
:
'
delimiter.html
'
},
],
],
],
])(
'
%s
'
,
(
string
,
tokens
)
=>
{
expect
(
editor
.
tokenize
(
string
,
'
vue
'
)).
toEqual
(
tokens
);
});
});
spec/frontend/ide/utils_spec.js
View file @
f4789c5c
import
{
commitItemIconMap
}
from
'
~/ide/constants
'
;
import
{
getCommitIconMap
,
isTextFile
}
from
'
~/ide/utils
'
;
import
{
getCommitIconMap
,
isTextFile
,
registerLanguages
}
from
'
~/ide/utils
'
;
import
{
decorateData
}
from
'
~/ide/stores/utils
'
;
import
{
languages
}
from
'
monaco-editor
'
;
describe
(
'
WebIDE utils
'
,
()
=>
{
describe
(
'
isTextFile
'
,
()
=>
{
...
...
@@ -102,4 +103,78 @@ describe('WebIDE utils', () => {
expect
(
getCommitIconMap
(
entry
)).
toEqual
(
commitItemIconMap
.
modified
);
});
});
describe
(
'
registerLanguages
'
,
()
=>
{
let
langs
;
beforeEach
(()
=>
{
langs
=
[
{
id
:
'
html
'
,
extensions
:
[
'
.html
'
],
conf
:
{
comments
:
{
blockComment
:
[
'
<!--
'
,
'
-->
'
]
}
},
language
:
{
tokenizer
:
{}
},
},
{
id
:
'
css
'
,
extensions
:
[
'
.css
'
],
conf
:
{
comments
:
{
blockComment
:
[
'
/*
'
,
'
*/
'
]
}
},
language
:
{
tokenizer
:
{}
},
},
{
id
:
'
js
'
,
extensions
:
[
'
.js
'
],
conf
:
{
comments
:
{
blockComment
:
[
'
/*
'
,
'
*/
'
]
}
},
language
:
{
tokenizer
:
{}
},
},
];
jest
.
spyOn
(
languages
,
'
register
'
).
mockImplementation
(()
=>
{});
jest
.
spyOn
(
languages
,
'
setMonarchTokensProvider
'
).
mockImplementation
(()
=>
{});
jest
.
spyOn
(
languages
,
'
setLanguageConfiguration
'
).
mockImplementation
(()
=>
{});
});
it
(
'
registers all the passed languages with Monaco
'
,
()
=>
{
registerLanguages
(...
langs
);
expect
(
languages
.
register
.
mock
.
calls
).
toEqual
([
[
{
conf
:
{
comments
:
{
blockComment
:
[
'
/*
'
,
'
*/
'
]
}
},
extensions
:
[
'
.css
'
],
id
:
'
css
'
,
language
:
{
tokenizer
:
{}
},
},
],
[
{
conf
:
{
comments
:
{
blockComment
:
[
'
/*
'
,
'
*/
'
]
}
},
extensions
:
[
'
.js
'
],
id
:
'
js
'
,
language
:
{
tokenizer
:
{}
},
},
],
[
{
conf
:
{
comments
:
{
blockComment
:
[
'
<!--
'
,
'
-->
'
]
}
},
extensions
:
[
'
.html
'
],
id
:
'
html
'
,
language
:
{
tokenizer
:
{}
},
},
],
]);
expect
(
languages
.
setMonarchTokensProvider
.
mock
.
calls
).
toEqual
([
[
'
css
'
,
{
tokenizer
:
{}
}],
[
'
js
'
,
{
tokenizer
:
{}
}],
[
'
html
'
,
{
tokenizer
:
{}
}],
]);
expect
(
languages
.
setLanguageConfiguration
.
mock
.
calls
).
toEqual
([
[
'
css
'
,
{
comments
:
{
blockComment
:
[
'
/*
'
,
'
*/
'
]
}
}],
[
'
js
'
,
{
comments
:
{
blockComment
:
[
'
/*
'
,
'
*/
'
]
}
}],
[
'
html
'
,
{
comments
:
{
blockComment
:
[
'
<!--
'
,
'
-->
'
]
}
}],
]);
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment