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
5da70294
Commit
5da70294
authored
Feb 08, 2022
by
Jacques Erasmus
Committed by
Brandon Labuschagne
Feb 08, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Pass language to the SourceViewer component
Passes the language field to the SourceViewer component
parent
105bd5f3
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
196 additions
and
38 deletions
+196
-38
app/assets/javascripts/repository/components/blob_viewers/index.js
...s/javascripts/repository/components/blob_viewers/index.js
+1
-1
app/assets/javascripts/repository/queries/blob_info.query.graphql
...ts/javascripts/repository/queries/blob_info.query.graphql
+1
-0
app/assets/javascripts/vue_shared/components/source_viewer/constants.js
...ascripts/vue_shared/components/source_viewer/constants.js
+111
-0
app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
...pts/vue_shared/components/source_viewer/source_viewer.vue
+12
-22
app/assets/javascripts/vue_shared/components/source_viewer/utils.js
.../javascripts/vue_shared/components/source_viewer/utils.js
+26
-0
qa/qa/page/component/blob_content.rb
qa/qa/page/component/blob_content.rb
+1
-1
spec/frontend/repository/components/blob_content_viewer_spec.js
...rontend/repository/components/blob_content_viewer_spec.js
+1
-1
spec/frontend/repository/mock_data.js
spec/frontend/repository/mock_data.js
+1
-0
spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js
...vue_shared/components/source_viewer/source_viewer_spec.js
+29
-13
spec/frontend/vue_shared/components/source_viewer/utils_spec.js
...rontend/vue_shared/components/source_viewer/utils_spec.js
+13
-0
No files found.
app/assets/javascripts/repository/components/blob_viewers/index.js
View file @
5da70294
...
...
@@ -3,7 +3,7 @@ const viewers = {
image
:
()
=>
import
(
'
./image_viewer.vue
'
),
video
:
()
=>
import
(
'
./video_viewer.vue
'
),
empty
:
()
=>
import
(
'
./empty_viewer.vue
'
),
text
:
()
=>
import
(
'
~/vue_shared/components/source_viewer.vue
'
),
text
:
()
=>
import
(
'
~/vue_shared/components/source_viewer
/source_viewer
.vue
'
),
pdf
:
()
=>
import
(
'
./pdf_viewer.vue
'
),
lfs
:
()
=>
import
(
'
./lfs_viewer.vue
'
),
};
...
...
app/assets/javascripts/repository/queries/blob_info.query.graphql
View file @
5da70294
...
...
@@ -20,6 +20,7 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) {
rawSize
rawTextBlob
fileType
language
path
editBlobPath
ideEditPath
...
...
app/assets/javascripts/vue_shared/components/source_viewer/constants.js
0 → 100644
View file @
5da70294
// Language map from Rouge::Lexer to highlight.js
// Rouge::Lexer - We use it on the BE to determine the language of a source file (https://github.com/rouge-ruby/rouge/blob/master/docs/Languages.md).
// Highlight.js - We use it on the FE to highlight the syntax of a source file (https://github.com/highlightjs/highlight.js/tree/main/src/languages).
export
const
ROUGE_TO_HLJS_LANGUAGE_MAP
=
{
bsl
:
'
1c
'
,
actionscript
:
'
actionscript
'
,
ada
:
'
ada
'
,
apache
:
'
apache
'
,
applescript
:
'
applescript
'
,
armasm
:
'
armasm
'
,
awk
:
'
awk
'
,
c
:
'
c
'
,
ceylon
:
'
ceylon
'
,
clean
:
'
clean
'
,
clojure
:
'
clojure
'
,
cmake
:
'
cmake
'
,
coffeescript
:
'
coffeescript
'
,
coq
:
'
coq
'
,
cpp
:
'
cpp
'
,
crystal
:
'
crystal
'
,
csharp
:
'
csharp
'
,
css
:
'
css
'
,
d
:
'
d
'
,
dart
:
'
dart
'
,
pascal
:
'
delphi
'
,
diff
:
'
diff
'
,
jinja
:
'
django
'
,
docker
:
'
dockerfile
'
,
batchfile
:
'
dos
'
,
elixir
:
'
elixir
'
,
elm
:
'
elm
'
,
erb
:
'
erb
'
,
erlang
:
'
erlang
'
,
fortran
:
'
fortran
'
,
fsharp
:
'
fsharp
'
,
gherkin
:
'
gherkin
'
,
glsl
:
'
glsl
'
,
go
:
'
go
'
,
gradle
:
'
gradle
'
,
groovy
:
'
groovy
'
,
haml
:
'
haml
'
,
handlebars
:
'
handlebars
'
,
haskell
:
'
haskell
'
,
haxe
:
'
haxe
'
,
http
:
'
http
'
,
hylang
:
'
hy
'
,
ini
:
'
ini
'
,
isbl
:
'
isbl
'
,
java
:
'
java
'
,
javascript
:
'
javascript
'
,
json
:
'
json
'
,
julia
:
'
julia
'
,
kotlin
:
'
kotlin
'
,
lasso
:
'
lasso
'
,
tex
:
'
latex
'
,
common_lisp
:
'
lisp
'
,
livescript
:
'
livescript
'
,
llvm
:
'
llvm
'
,
hlsl
:
'
lsl
'
,
lua
:
'
lua
'
,
make
:
'
makefile
'
,
markdown
:
'
markdown
'
,
mathematica
:
'
mathematica
'
,
matlab
:
'
matlab
'
,
moonscript
:
'
moonscript
'
,
nginx
:
'
nginx
'
,
nim
:
'
nim
'
,
nix
:
'
nix
'
,
objective_c
:
'
objectivec
'
,
ocaml
:
'
ocaml
'
,
perl
:
'
perl
'
,
php
:
'
php
'
,
plaintext
:
'
plaintext
'
,
pony
:
'
pony
'
,
powershell
:
'
powershell
'
,
prolog
:
'
prolog
'
,
properties
:
'
properties
'
,
protobuf
:
'
protobuf
'
,
puppet
:
'
puppet
'
,
python
:
'
python
'
,
q
:
'
q
'
,
qml
:
'
qml
'
,
r
:
'
r
'
,
reasonml
:
'
reasonml
'
,
ruby
:
'
ruby
'
,
rust
:
'
rust
'
,
sas
:
'
sas
'
,
scala
:
'
scala
'
,
scheme
:
'
scheme
'
,
scss
:
'
scss
'
,
shell
:
'
shell
'
,
smalltalk
:
'
smalltalk
'
,
sml
:
'
sml
'
,
sqf
:
'
sqf
'
,
sql
:
'
sql
'
,
stan
:
'
stan
'
,
stata
:
'
stata
'
,
swift
:
'
swift
'
,
tap
:
'
tap
'
,
tcl
:
'
tcl
'
,
twig
:
'
twig
'
,
typescript
:
'
typescript
'
,
vala
:
'
vala
'
,
vb
:
'
vbnet
'
,
verilog
:
'
verilog
'
,
vhdl
:
'
vhdl
'
,
viml
:
'
vim
'
,
xml
:
'
xml
'
,
xquery
:
'
xquery
'
,
yaml
:
'
yaml
'
,
};
app/assets/javascripts/vue_shared/components/source_viewer.vue
→
app/assets/javascripts/vue_shared/components/source_viewer
/source_viewer
.vue
View file @
5da70294
<
script
>
import
{
GlSafeHtmlDirective
}
from
'
@gitlab/ui
'
;
import
{
GlSafeHtmlDirective
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
LineNumbers
from
'
~/vue_shared/components/line_numbers.vue
'
;
import
{
sanitize
}
from
'
~/lib/dompurify
'
;
import
{
ROUGE_TO_HLJS_LANGUAGE_MAP
}
from
'
./constants
'
;
import
{
wrapLines
}
from
'
./utils
'
;
const
LINE_SELECT_CLASS_NAME
=
'
hll
'
;
const
PLAIN_TEXT_LANGUAGE
=
'
plaintext
'
;
export
default
{
components
:
{
LineNumbers
,
GlLoadingIcon
,
},
directives
:
{
SafeHtml
:
GlSafeHtmlDirective
,
...
...
@@ -18,17 +20,12 @@ export default {
type
:
Object
,
required
:
true
,
},
autoDetect
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
// We'll eventually disable autoDetect and pass the language explicitly to reduce the footprint (https://gitlab.com/gitlab-org/gitlab/-/issues/348145)
},
},
data
()
{
return
{
languageDefinition
:
null
,
content
:
this
.
blob
.
rawTextBlob
,
language
:
this
.
blob
.
language
||
PLAIN_TEXT_LANGUAGE
,
language
:
ROUGE_TO_HLJS_LANGUAGE_MAP
[
this
.
blob
.
language
]
,
hljs
:
null
,
};
},
...
...
@@ -40,14 +37,14 @@ export default {
let
highlightedContent
;
if
(
this
.
hljs
)
{
if
(
this
.
autoDetect
)
{
if
(
!
this
.
language
)
{
highlightedContent
=
this
.
hljs
.
highlightAuto
(
this
.
content
).
value
;
}
else
if
(
this
.
languageDefinition
)
{
highlightedContent
=
this
.
hljs
.
highlight
(
this
.
content
,
{
language
:
this
.
language
}).
value
;
}
}
return
this
.
wrapLines
(
highlightedContent
);
return
wrapLines
(
highlightedContent
);
},
},
watch
:
{
...
...
@@ -61,14 +58,14 @@ export default {
async
mounted
()
{
this
.
hljs
=
await
this
.
loadHighlightJS
();
if
(
!
this
.
autoDetect
)
{
if
(
this
.
language
)
{
this
.
languageDefinition
=
await
this
.
loadLanguage
();
}
},
methods
:
{
loadHighlightJS
()
{
//
With auto-detect enabled
we load all common languages else we load only the core (smallest footprint)
return
this
.
autoDetect
?
import
(
'
highlight.js/lib/common
'
)
:
import
(
'
highlight.js/lib/core
'
);
//
If no language can be mapped to highlight.js
we load all common languages else we load only the core (smallest footprint)
return
!
this
.
language
?
import
(
'
highlight.js/lib/common
'
)
:
import
(
'
highlight.js/lib/core
'
);
},
async
loadLanguage
()
{
let
languageDefinition
;
...
...
@@ -82,15 +79,6 @@ export default {
return
languageDefinition
;
},
wrapLines
(
content
)
{
return
(
content
&&
content
.
split
(
'
\n
'
)
.
map
((
line
,
i
)
=>
`<span id="LC
${
i
+
1
}
" class="line">
${
line
}
</span>`
)
.
join
(
'
\r\n
'
)
);
},
selectLine
()
{
const
hash
=
sanitize
(
this
.
$route
.
hash
);
const
lineToSelect
=
hash
&&
this
.
$el
.
querySelector
(
hash
);
...
...
@@ -113,7 +101,9 @@ export default {
};
</
script
>
<
template
>
<gl-loading-icon
v-if=
"!highlightedContent"
size=
"sm"
class=
"gl-my-5"
/>
<div
v-else
class=
"file-content code js-syntax-highlight blob-content gl-display-flex"
:class=
"$options.userColorScheme"
data-type=
"simple"
...
...
app/assets/javascripts/vue_shared/components/source_viewer/utils.js
0 → 100644
View file @
5da70294
export
const
wrapLines
=
(
content
)
=>
{
return
(
content
&&
content
.
split
(
'
\n
'
)
.
map
((
line
,
i
)
=>
{
let
formattedLine
;
const
idAttribute
=
`id="LC
${
i
+
1
}
"`
;
if
(
line
.
includes
(
'
<span class="hljs
'
)
&&
!
line
.
includes
(
'
</span>
'
))
{
/**
* In some cases highlight.js will wrap multiple lines in a span, in these cases we want to append the line number to the existing span
*
* example (before): <span class="hljs-code">```bash
* example (after): <span id="LC67" class="hljs-code">```bash
*/
formattedLine
=
line
.
replace
(
/
(?=
class="hljs
)
/
,
`
${
idAttribute
}
`
);
}
else
{
formattedLine
=
`<span
${
idAttribute
}
class="line">
${
line
}
</span>`
;
}
return
formattedLine
;
})
.
join
(
'
\n
'
)
);
};
qa/qa/page/component/blob_content.rb
View file @
5da70294
...
...
@@ -22,7 +22,7 @@ module QA
element
:copy_contents_button
end
base
.
view
'app/assets/javascripts/vue_shared/components/source_viewer.vue'
do
base
.
view
'app/assets/javascripts/vue_shared/components/source_viewer
/source_viewer
.vue'
do
element
:blob_viewer_file_content
end
...
...
spec/frontend/repository/components/blob_content_viewer_spec.js
View file @
5da70294
...
...
@@ -15,7 +15,7 @@ import ForkSuggestion from '~/repository/components/fork_suggestion.vue';
import
{
loadViewer
}
from
'
~/repository/components/blob_viewers
'
;
import
DownloadViewer
from
'
~/repository/components/blob_viewers/download_viewer.vue
'
;
import
EmptyViewer
from
'
~/repository/components/blob_viewers/empty_viewer.vue
'
;
import
SourceViewer
from
'
~/vue_shared/components/source_viewer.vue
'
;
import
SourceViewer
from
'
~/vue_shared/components/source_viewer
/source_viewer
.vue
'
;
import
blobInfoQuery
from
'
~/repository/queries/blob_info.query.graphql
'
;
import
{
redirectTo
}
from
'
~/lib/utils/url_utility
'
;
import
{
isLoggedIn
}
from
'
~/lib/utils/common_utils
'
;
...
...
spec/frontend/repository/mock_data.js
View file @
5da70294
...
...
@@ -5,6 +5,7 @@ export const simpleViewerMock = {
rawSize
:
123
,
rawTextBlob
:
'
raw content
'
,
fileType
:
'
text
'
,
language
:
'
javascript
'
,
path
:
'
some_file.js
'
,
webPath
:
'
some_file.js
'
,
editBlobPath
:
'
some_file.js/edit
'
,
...
...
spec/frontend/vue_shared/components/source_viewer_spec.js
→
spec/frontend/vue_shared/components/source_viewer
/source_viewer
_spec.js
View file @
5da70294
import
hljs
from
'
highlight.js/lib/core
'
;
import
{
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
VueRouter
from
'
vue-router
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
SourceViewer
from
'
~/vue_shared/components/source_viewer.vue
'
;
import
SourceViewer
from
'
~/vue_shared/components/source_viewer/source_viewer.vue
'
;
import
{
ROUGE_TO_HLJS_LANGUAGE_MAP
}
from
'
~/vue_shared/components/source_viewer/constants
'
;
import
LineNumbers
from
'
~/vue_shared/components/line_numbers.vue
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
...
...
@@ -12,43 +14,50 @@ const router = new VueRouter();
describe
(
'
Source Viewer component
'
,
()
=>
{
let
wrapper
;
const
language
=
'
javascript
'
;
const
language
=
'
docker
'
;
const
mappedLanguage
=
ROUGE_TO_HLJS_LANGUAGE_MAP
[
language
];
const
content
=
`// Some source code`
;
const
DEFAULT_BLOB_DATA
=
{
language
,
rawTextBlob
:
content
};
const
highlightedContent
=
`<span data-testid='test-highlighted' id='LC1'>
${
content
}
</span><span id='LC2'></span>`
;
hljs
.
highlight
.
mockImplementation
(()
=>
({
value
:
highlightedContent
}));
hljs
.
highlightAuto
.
mockImplementation
(()
=>
({
value
:
highlightedContent
}));
const
createComponent
=
async
(
props
=
{
autoDetect
:
false
})
=>
{
const
createComponent
=
async
(
blob
=
{})
=>
{
wrapper
=
shallowMountExtended
(
SourceViewer
,
{
router
,
propsData
:
{
blob
:
{
...
DEFAULT_BLOB_DATA
},
...
props
},
propsData
:
{
blob
:
{
...
DEFAULT_BLOB_DATA
,
...
blob
}
},
});
await
waitForPromises
();
};
const
findLoadingIcon
=
()
=>
wrapper
.
findComponent
(
GlLoadingIcon
);
const
findLineNumbers
=
()
=>
wrapper
.
findComponent
(
LineNumbers
);
const
findHighlightedContent
=
()
=>
wrapper
.
findByTestId
(
'
test-highlighted
'
);
const
findFirstLine
=
()
=>
wrapper
.
find
(
'
#LC1
'
);
beforeEach
(()
=>
createComponent
());
beforeEach
(()
=>
{
hljs
.
highlight
.
mockImplementation
(()
=>
({
value
:
highlightedContent
}));
hljs
.
highlightAuto
.
mockImplementation
(()
=>
({
value
:
highlightedContent
}));
return
createComponent
();
});
afterEach
(()
=>
wrapper
.
destroy
());
describe
(
'
highlight.js
'
,
()
=>
{
it
(
'
registers the language definition
'
,
async
()
=>
{
const
languageDefinition
=
await
import
(
`highlight.js/lib/languages/
${
l
anguage
}
`
);
const
languageDefinition
=
await
import
(
`highlight.js/lib/languages/
${
mappedL
anguage
}
`
);
expect
(
hljs
.
registerLanguage
).
toHaveBeenCalledWith
(
language
,
languageDefinition
.
default
);
expect
(
hljs
.
registerLanguage
).
toHaveBeenCalledWith
(
mappedLanguage
,
languageDefinition
.
default
,
);
});
it
(
'
highlights the content
'
,
()
=>
{
expect
(
hljs
.
highlight
).
toHaveBeenCalledWith
(
content
,
{
language
});
expect
(
hljs
.
highlight
).
toHaveBeenCalledWith
(
content
,
{
language
:
mappedLanguage
});
});
describe
(
'
auto-detect
enabl
ed
'
,
()
=>
{
beforeEach
(()
=>
createComponent
({
autoDetect
:
true
}));
describe
(
'
auto-detect
s if a language cannot be load
ed
'
,
()
=>
{
beforeEach
(()
=>
createComponent
({
language
:
'
some_unknown_language
'
}));
it
(
'
highlights the content with auto-detection
'
,
()
=>
{
expect
(
hljs
.
highlightAuto
).
toHaveBeenCalledWith
(
content
);
...
...
@@ -57,6 +66,13 @@ describe('Source Viewer component', () => {
});
describe
(
'
rendering
'
,
()
=>
{
it
(
'
renders a loading icon if no highlighted content is available yet
'
,
async
()
=>
{
hljs
.
highlight
.
mockImplementation
(()
=>
({
value
:
null
}));
await
createComponent
();
expect
(
findLoadingIcon
().
exists
()).
toBe
(
true
);
});
it
(
'
renders Line Numbers
'
,
()
=>
{
expect
(
findLineNumbers
().
props
(
'
lines
'
)).
toBe
(
1
);
});
...
...
spec/frontend/vue_shared/components/source_viewer/utils_spec.js
0 → 100644
View file @
5da70294
import
{
wrapLines
}
from
'
~/vue_shared/components/source_viewer/utils
'
;
describe
(
'
Wrap lines
'
,
()
=>
{
it
.
each
`
input | output
${
'
line 1
'
}
|
${
'
<span id="LC1" class="line">line 1</span>
'
}
${
'
line 1
\n
line 2
'
}
|
${
`<span id="LC1" class="line">line 1</span>\n<span id="LC2" class="line">line 2</span>`
}
${
'
<span class="hljs-code">line 1
\n
line 2</span>
'
}
|
${
`<span id="LC1" class="hljs-code">line 1\n<span id="LC2" class="line">line 2</span></span>`
}
${
'
<span class="hljs-code">```bash
'
}
|
${
'
<span id="LC1" class="hljs-code">```bash
'
}
`
(
'
returns lines wrapped in spans containing line numbers
'
,
({
input
,
output
})
=>
{
expect
(
wrapLines
(
input
)).
toBe
(
output
);
});
});
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