summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
Diffstat (limited to 'webapp')
-rw-r--r--webapp/components/code_preview.jsx49
-rw-r--r--webapp/sass/layout/_markdown.scss22
-rw-r--r--webapp/utils/markdown.jsx49
-rw-r--r--webapp/utils/syntax_hightlighting.jsx83
4 files changed, 121 insertions, 82 deletions
diff --git a/webapp/components/code_preview.jsx b/webapp/components/code_preview.jsx
index e769ae590..6e1af262f 100644
--- a/webapp/components/code_preview.jsx
+++ b/webapp/components/code_preview.jsx
@@ -2,11 +2,14 @@
// See License.txt for license information.
import $ from 'jquery';
-import * as syntaxHightlighting from 'utils/syntax_hightlighting.jsx';
+import React from 'react';
+
+import * as SyntaxHighlighting from 'utils/syntax_hightlighting.jsx';
import Constants from 'utils/constants.jsx';
+
import FileInfoPreview from './file_info_preview.jsx';
-import React from 'react';
+import loadingGif from 'images/load.gif';
export default class CodePreview extends React.Component {
constructor(props) {
@@ -35,7 +38,7 @@ export default class CodePreview extends React.Component {
}
updateStateFromProps(props) {
- var usedLanguage = syntaxHightlighting.getLang(props.filename);
+ var usedLanguage = SyntaxHighlighting.getLang(props.filename);
if (!usedLanguage || props.fileInfo.size > Constants.CODE_PREVIEW_MAX_FILE_SIZE) {
this.setState({code: '', lang: '', loading: false, success: false});
@@ -54,8 +57,7 @@ export default class CodePreview extends React.Component {
}
handleReceivedCode(data) {
- const parsed = syntaxHightlighting.formatCode(this.state.lang, data, this.props.filename);
- this.setState({code: parsed, loading: false, success: true});
+ this.setState({code: data, loading: false, success: true});
}
handleReceivedError() {
@@ -63,7 +65,7 @@ export default class CodePreview extends React.Component {
}
static support(filename) {
- return typeof syntaxHightlighting.getLang(filename) !== 'undefined';
+ return !!SyntaxHighlighting.getLanguageFromFilename(filename);
}
render() {
@@ -72,7 +74,7 @@ export default class CodePreview extends React.Component {
<div className='view-image__loading'>
<img
className='loader-image'
- src='/static/images/load.gif'
+ src={loadingGif}
/>
</div>
);
@@ -89,7 +91,38 @@ export default class CodePreview extends React.Component {
);
}
- return <div dangerouslySetInnerHTML={{__html: this.state.code}}/>;
+ // add line numbers when viewing a code file preview
+ const lines = this.state.code.match(/\r\n|\r|\n|$/g).length;
+ let strlines = '';
+ for (let i = 1; i <= lines; i++) {
+ if (strlines) {
+ strlines += '\n' + i;
+ } else {
+ strlines += i;
+ }
+ }
+
+ const language = SyntaxHighlighting.getLanguageName(this.state.lang);
+
+ const highlighted = SyntaxHighlighting.highlight(this.state.lang, this.state.code);
+
+ return (
+ <div className='post-code'>
+ <span className='post-code__language'>
+ {`${this.props.filename} - ${language}`}
+ </span>
+ <code className='hljs'>
+ <table>
+ <tbody>
+ <tr>
+ <td className='post-code__lineno'>{strlines}</td>
+ <td dangerouslySetInnerHTML={{__html: highlighted}}/>
+ </tr>
+ </tbody>
+ </table>
+ </code>
+ </div>
+ );
}
}
diff --git a/webapp/sass/layout/_markdown.scss b/webapp/sass/layout/_markdown.scss
index 9bac332d6..04c4434ac 100644
--- a/webapp/sass/layout/_markdown.scss
+++ b/webapp/sass/layout/_markdown.scss
@@ -26,16 +26,13 @@
overflow-y: hidden;
position: relative;
- pre {
- border: 1px solid rgba(221,221,221,0.2);
+ code {
+ border: 1px solid rgba(221,221,221,1);
border-radius: .25em;
+ font-size: 13px;
margin: 5px 0;
- padding: 0;
+ padding: 6.5px;
text-align: left;
- }
-
- code {
- border: none;
white-space: pre;
}
@@ -51,6 +48,10 @@
&--wrap code {
white-space: pre-wrap;
}
+
+ .hljs {
+ position: relative;
+ }
}
.post-code__language {
@@ -78,6 +79,13 @@
user-select: none;
}
+.post-code__search-highlighting {
+ color: transparent;
+ pointer-events: none;
+ position: absolute;
+ @include user-select(none);
+}
+
.post__body {
hr {
@include opacity(.2);
diff --git a/webapp/utils/markdown.jsx b/webapp/utils/markdown.jsx
index 7fd165134..6691b57b2 100644
--- a/webapp/utils/markdown.jsx
+++ b/webapp/utils/markdown.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import * as TextFormatting from './text_formatting.jsx';
-import * as syntaxHightlighting from './syntax_hightlighting.jsx';
+import * as SyntaxHighlighting from './syntax_hightlighting.jsx';
import marked from 'marked';
import katex from 'katex';
@@ -43,7 +43,52 @@ class MattermostMarkdownRenderer extends marked.Renderer {
usedLanguage = 'xml';
}
- return syntaxHightlighting.formatCode(usedLanguage, code, null, this.formattingOptions.searchTerm);
+ let className = 'post-code';
+ if (!usedLanguage) {
+ className += ' post-code--wrap';
+ }
+
+ let header = '';
+ if (SyntaxHighlighting.canHighlight(usedLanguage)) {
+ header = (
+ '<span class="post-code__language">' +
+ SyntaxHighlighting.getLanguageName(language) +
+ '</span>'
+ );
+ }
+
+ // if we have to apply syntax highlighting AND highlighting of search terms, create two copies
+ // of the code block, one with syntax highlighting applied and another with invisible text, but
+ // search term highlighting and overlap them
+ const content = SyntaxHighlighting.highlight(usedLanguage, code);
+ let searchedContent = '';
+
+ if (this.formattingOptions.searchTerm) {
+ const tokens = new Map();
+
+ let searched = TextFormatting.sanitizeHtml(code);
+ searched = TextFormatting.highlightSearchTerms(searched, tokens, this.formattingOptions.searchTerm);
+
+ if (tokens.size > 0) {
+ searched = TextFormatting.replaceTokens(searched, tokens);
+
+ searchedContent = (
+ '<div class="post-code__search-highlighting">' +
+ searched +
+ '</div>'
+ );
+ }
+ }
+
+ return (
+ '<div class="' + className + '">' +
+ header +
+ '<code class="hljs">' +
+ searchedContent +
+ content +
+ '</code>' +
+ '</div>'
+ );
}
codespan(text) {
diff --git a/webapp/utils/syntax_hightlighting.jsx b/webapp/utils/syntax_hightlighting.jsx
index 33b3e2d3d..ce904c41f 100644
--- a/webapp/utils/syntax_hightlighting.jsx
+++ b/webapp/utils/syntax_hightlighting.jsx
@@ -123,80 +123,21 @@ hlJS.registerLanguage('yaml', hljsYaml);
const HighlightedLanguages = Constants.HighlightedLanguages;
-export function formatCode(lang, data, filename, searchTerm) {
- const language = lang.toLowerCase() || '';
-
- let contents;
- let header = '';
- let className = 'post-code';
+export function highlight(lang, code) {
+ const language = lang.toLowerCase();
if (HighlightedLanguages[language]) {
- let name = HighlightedLanguages[language].name;
-
- if (filename) {
- const fname = decodeURIComponent(Utils.getFileName(filename));
- name = fname + ' - ' + name;
- }
-
- header = '<span class="post-code__language">' + name + '</span>';
-
try {
- contents = hlJS.highlight(language, data).value;
+ return hlJS.highlight(language, code).value;
} catch (e) {
- contents = TextFormatting.sanitizeHtml(data);
+ // fall through if highlighting fails and handle below
}
- } else {
- contents = TextFormatting.sanitizeHtml(data);
}
- if (!language) {
- // wrap when no language is specified
- className += ' post-code--wrap';
-
- const tokens = new Map();
- contents = TextFormatting.highlightSearchTerms(contents, tokens, searchTerm);
- contents = TextFormatting.replaceTokens(contents, tokens);
- }
-
- if (filename) {
- // add line numbers when viewing a code file preview
- const lines = data.match(/\r\n|\r|\n|$/g).length;
- let strlines = '';
- for (let i = 1; i <= lines; i++) {
- if (strlines) {
- strlines += '\n' + i;
- } else {
- strlines += i;
- }
- }
-
- contents = (
- '<table>' +
- '<tbody>' +
- '<tr>' +
- '<td class="post-code__lineno">' + strlines + '</td>' +
- '<td>' +
- contents +
- '</td>' +
- '</tr>' +
- '</tbody>' +
- '</table>'
- );
- }
-
- return (
- '<div class="' + className + '">' +
- header +
- '<pre>' +
- '<code class="hljs">' +
- contents +
- '</code>' +
- '</pre>' +
- '</div>'
- );
+ return TextFormatting.sanitizeHtml(code);
}
-export function getLang(filename) {
+export function getLanguageFromFilename(filename) {
const fileInfo = Utils.splitFileLocation(filename);
var ext = fileInfo.ext;
if (!ext) {
@@ -211,3 +152,15 @@ export function getLang(filename) {
}
return null;
}
+
+export function canHighlight(language) {
+ return !!HighlightedLanguages[language.toLowerCase()];
+}
+
+export function getLanguageName(language) {
+ if (canHighlight(language)) {
+ return HighlightedLanguages[language.toLowerCase()].name;
+ }
+
+ return '';
+} \ No newline at end of file