Skip to content

Commit

Permalink
feat(new-ui): Syntax Highlighting and Theme support
Browse files Browse the repository at this point in the history
* No Logic Changes. Refactored syntax-highlight feature into code shared between new and old uis

* No Logic  Changes.  Renamed syntaxHighlight to syntaxHighlightOld

* Implemented Syntax Highlighting for New PR UI

* Adds Prism Theme CSS files to solution

* Commenting Out Code blocks as they don't play nicely with BB's line by line html structure

On the old diff tools, this results in backgrounds that are
intended to be applied once for an entire code block
instead appearing line by line, resulting in very weird
patterns and bordering issues

* Implements syntax highlighting themes

* Updated New UI Syntax Implementation to Conserve Word Highlighting

The previous implementation included an optimized way of handling DOM updates,
but it resulted in erasing child elements of each line of code.  Bitbucket handles
word comparison highlighting by inserting <ins> and <del> into the code,
therefore these were lost.

The current implementation now highlights each row of code in-place on the DOM, taking a
small performance decrease but allowing the Prism library to ensure that <ins> and <del>
are respected and word highlighting is not lost.

Co-authored-by: Ronald Rey <reyronald@gmail.com>
  • Loading branch information
RobertChrist and reyronald committed Jan 31, 2021
1 parent b67ab69 commit c3c339c
Show file tree
Hide file tree
Showing 25 changed files with 902 additions and 68 deletions.
1 change: 1 addition & 0 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const justInstalledOrUpdated = new Promise((resolve, reject) => {
new OptionsSync().define({
defaults: {
syntaxHighlight: true,
syntaxHighlightTheme: 'prism',
compactFileTree: false,
autolinker: true,
highlightOcurrences: true,
Expand Down
2 changes: 1 addition & 1 deletion src/diff-ignore/diff-ignore.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import elementReady from 'element-ready'
import ignore, { type Ignore } from 'ignore'
import { h } from 'dom-chef'
import { getFilepathFromElement } from '../syntax-highlight/source-handler'
import { getFilepathFromElement } from '../syntax-highlight/old-ui/get-file-path'

let ig: Ignore

Expand Down
21 changes: 19 additions & 2 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import pullrequestCommitAmount from './pullrequest-commit-amount'
import insertPullrequestTemplate from './pullrequest-template'
import insertShowComments from './show-comments'
import addSidebarCounters from './sidebar-counters'
import syntaxHighlight from './syntax-highlight'
import { syntaxHighlightOldUI, syntaxHighlightNewUI } from './syntax-highlight'
import comparePagePullRequest from './compare-page-pull-request'
import setTabSize from './tab-size'
import mergeCommitMessage from './merge-commit-message'
Expand Down Expand Up @@ -199,7 +199,11 @@ function codeReviewFeatures(config) {
}

if (config.syntaxHighlight) {
syntaxHighlight(diff, afterWordDiff)
syntaxHighlightOldUI(
diff,
afterWordDiff,
config.syntaxHighlightTheme
)
}
}
}
Expand Down Expand Up @@ -283,6 +287,19 @@ function pullrequestRelatedFeaturesNew(config) {
if (config.compactFileTree) {
setCompactPRFileTree()
}

if (config.syntaxHighlight) {
// eslint-disable-next-line no-new
new SelectorObserver(
document.body,
'section[aria-label="Diffs"]',
function() {
if (config.syntaxHighlight) {
syntaxHighlightNewUI(this, config.syntaxHighlightTheme)
}
}
)
}
}

function pullrequestRelatedFeaturesOld(config) {
Expand Down
48 changes: 48 additions & 0 deletions src/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,54 @@
<br />
<br />

<label><strong>Syntax Highlighting Theme Options</strong></label>
<div>
Examples can be tried at <a href="https://prismjs.com/">https://prismjs.com/</a>
</div>
<div name="syntaxHighlightThemes" style="margin: 0.5em 1em 0 0;">
<label for="default" style="margin: .5em 1em 0 0">
Default:
<input type="radio" name="syntaxHighlightTheme" value="prism" />
</label>

<label for="prism-coy" style="margin: .5em 1em 0 0">
Coy:
<input type="radio" name="syntaxHighlightTheme" value="prism-coy" />
</label>

<label for="prism-solarizedlight" style="margin: .5em 1em 0 0">Solarized
Light:
<input type="radio" name="syntaxHighlightTheme" value="prism-solarizedlight" />
</label>

<label for="prism-tomorrow" style="margin: .5em 1em 0 0">
Tomorrow:
<input type="radio" name="syntaxHighlightTheme" value="prism-tomorrow" />
</label>

<label for="prism-dark" style="margin: .5em 1em 0 0">
Dark:
<input type="radio" name="syntaxHighlightTheme" value="prism-dark" />
</label>

<label for="prism-twilight" style="margin: .5em 1em 0 0">
Twilight:
<input type="radio" name="syntaxHighlightTheme" value="prism-twilight" />
</label>

<label for="prism-okaidia" style="margin: .5em 1em 0 0">
Okaidia:
<input type="radio" name="syntaxHighlightTheme" value="prism-okaidia" />
</label>

<label for="prism-funky" style="margin: .5em 1em 0 0">
Funky:
<input type="radio" name="syntaxHighlightTheme" value="prism-funky" />
</label>
</div>
<br />
<br />

<label>
<input type="checkbox" name="compactFileTree" /> Compact View Mode for PR FileTrees (Removes padding from
files, displays more files on screen)
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import languagesExtensions from './language-ext'
declare var Prism: { languages: { [language: string]: Object } }

/**
* Retrieves a class according to the element data-filename or data-path attribute.
* For example, calling it passing an element with a data-filename (or data-path)
* like "/path/to/file/filename.java" would return "language-java".
* Retrieves a class according to a filepath string argument.
* For example, calling it passing a string such as:
* "/path/to/file/filename.java" would return "language-java".
*
* @param {HTMLElement} element An HTML element. Pass anything different and bear the consequences :)
* @param {string} filePath A file name and (optionally) path. For example C:/HellowWorld.Java or just HelloWorld.Java
* @return {string} The class extracted from the element's file path.
*/
export function getLanguageClass(element: HTMLElement): string {
const filePath = getFilepathFromElement(element)
export function getLanguageClass(filePath: string): string {
const fileExtension = getExtension(filePath).toLowerCase()

if (fileExtension in languagesExtensions) {
Expand All @@ -34,21 +33,6 @@ export function getLanguageClass(element: HTMLElement): string {
return ''
}

/**
* Retrieves the filename of an element according to its `data-identifier`, * `data-filename` or `data-path` attributes.
*
* @param {HTMLElement} element An HTML element. Pass anything different and bear the consequences :)
* @return {string} The filename
*/
export function getFilepathFromElement(element: HTMLElement): string {
const filepath =
element.getAttribute('data-identifier') ||
element.getAttribute('data-filename') ||
element.getAttribute('data-path') ||
''
return filepath.trim()
}

/**
* @param {string} filepath Filepath
* @return {string} Extension
Expand Down
53 changes: 53 additions & 0 deletions src/syntax-highlight/common/source-handler.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import test from 'ava'

import * as sourceHandler from './source-handler'

import '../../vendor/prism'

test('Returns a language-xxxx class from the native Prism languages list', t => {
const languageClass = sourceHandler.getLanguageClass(
'z/path/to/file/the-file.java'
)

t.is(
languageClass,
'language-java',
'proper language-xxxx class returned from native Prism languages list'
)
})

test('Returns a language-xxx from the language extensions file', t => {
const languageClass = sourceHandler.getLanguageClass(
'filepath/withALanguage/Extension/like.coffee'
)

t.is(
languageClass,
'language-coffeescript',
'proper language-cofeescript returned from language extension list'
)
})

test('Returns a language-ruby class when handling non-extension but globally file named Vagrantfiles', t => {
const languageClass = sourceHandler.getLanguageClass(
'z/path/to/Vagrantfile'
)

t.is(
languageClass,
'language-ruby',
'proper language-ruby class returned for globally named vagrantfile'
)
})

test('Returns a language-ruby class when handling non-extension but globally file named named Jenknsfiles', t => {
const languageClass = sourceHandler.getLanguageClass(
'z/path/to/Jenkinsfile'
)

t.is(
languageClass,
'language-groovy',
'proper language-groovy returned for globally named Jenknsfile'
)
})
5 changes: 4 additions & 1 deletion src/syntax-highlight/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export { default } from './syntax-highlight'
// @flow

export { default as syntaxHighlightOldUI } from './syntax-highlight-old'
export { default as syntaxHighlightNewUI } from './syntax-highlight-new'
File renamed without changes.
16 changes: 16 additions & 0 deletions src/syntax-highlight/old-ui/get-file-path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @flow

/**
* Retrieves the filename of an element according to its `data-identifier`, * `data-filename` or `data-path` attributes.
*
* @param {HTMLElement} element An HTML element. Pass anything different and bear the consequences :)
* @return {string} The filename
*/
export function getFilepathFromElement(element: HTMLElement): string {
const filepath =
element.getAttribute('data-identifier') ||
element.getAttribute('data-filename') ||
element.getAttribute('data-path') ||
''
return filepath.trim()
}
16 changes: 16 additions & 0 deletions src/syntax-highlight/old-ui/get-file-path.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @jsx h

import test from 'ava'
import { h } from 'dom-chef'

import * as getFilePath from './get-file-path'
import '../../../test/setup-jsdom'

test('Test reading the filepath from a PR (Old UI) element', t => {
const filePath = 'z/path/to/file/the-file.java'
const element = <div data-filename={filePath} />

const res = getFilePath.getFilepathFromElement(element)

t.is(res, filePath, 'Test reading the filepath from a PR (Old UI) element')
})
45 changes: 45 additions & 0 deletions src/syntax-highlight/prism-themes/load-theme-once.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// @flow

'use strict'

let stylesImported = false

export default function loadThemeOnce(themeName: string) {
if (stylesImported) return

switch (themeName) {
case 'prism-coy':
require('./prism-coy.css')
break

case 'prism-dark':
require('./prism-dark.css')
break

case 'prism-funky':
require('./prism-funky.css')
break

case 'prism-okaidia':
require('./prism-okaidia.css')
break

case 'prism-solarizedlight':
require('./prism-solarizedlight.css')
break

case 'prism-tomorrow':
require('./prism-tomorrow.css')
break

case 'prism-twilight':
require('./prism-twilight.css')
break

case 'prism':
default:
require('./prism.css')
}

stylesImported = true
}
86 changes: 86 additions & 0 deletions src/syntax-highlight/prism-themes/prism-coy.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* prism.js Coy theme for JavaScript, CoffeeScript, CSS and HTML
* Based on https://github.com/tshedor/workshop-wp-theme (Example: http://workshop.kansan.com/category/sessions/basics or http://workshop.timshedor.com/category/sessions/basics);
* @author Tim Shedor
*
* Updated by Robert Christ for Bitbucket-Refined.
* I took this file, and in the current commit, removed all components
* that do not play nicely with Bitbucket.
*/
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #7d8b99;
}

.token.punctuation {
color: #5f6364;
}

.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.function-name,
.token.constant,
.token.symbol,
.token.deleted {
color: #c92c2c;
}

.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.function,
.token.builtin,
.token.inserted {
color: #2f9c0a;
}

.token.operator,
.token.entity,
.token.url,
.token.variable {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}

.token.atrule,
.token.attr-value,
.token.keyword,
.token.class-name {
color: #1990b8;
}

.token.regex,
.token.important {
color: #e90;
}

.language-css .token.string,
.style .token.string {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}

.token.important {
font-weight: normal;
}

.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}

.token.entity {
cursor: help;
}

.namespace {
opacity: 0.7;
}

0 comments on commit c3c339c

Please sign in to comment.