Last active
August 17, 2020 06:00
-
-
Save lights0123/51c9eb04d3de80529b01741ec4137bd7 to your computer and use it in GitHub Desktop.
vue-highlight with line numbers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<pre><code :style="{ tabSize, fontSize: `${fontSize}px` }" :class="['hljs', lang]" v-html="formattedCode"></code></pre> | |
</template> | |
<script lang="ts"> | |
import { Component, Prop, Vue, Watch } from 'vue-property-decorator'; | |
import hljs from 'highlight.js'; | |
import highlightLines from './highlightLines'; | |
@Component | |
export default class Code extends Vue { | |
@Prop(String) lang!: string; | |
@Prop(String) code!: string; | |
@Prop({ type: Boolean, default: true }) lineNumbers!: boolean; | |
@Prop({ type: Number, default: 4 }) tabSize!: number; | |
@Prop({ type: Number, default: 14 }) fontSize!: number; | |
private formattedCode = ''; | |
public mounted() { | |
this.onCodeUpdate(); | |
} | |
@Watch('code') | |
@Watch('lang') | |
@Watch('lineNumbers') | |
// highlightLines must be called in the browser | |
private onCodeUpdate() { | |
let code = hljs.highlight(this.lang, this.code).value; | |
if (this.code.length > 0 && this.lineNumbers) { | |
code = highlightLines(code); | |
} | |
this.formattedCode = code; | |
} | |
}; | |
</script> | |
<style lang="scss"> | |
.hljs-ln { | |
td { | |
padding: 0; | |
} | |
border-collapse: collapse; | |
line-height: 1.429em; | |
} | |
.hljs-ln-n:before { | |
content: attr(data-line-number) | |
} | |
/* for block of numbers */ | |
.hljs-ln-numbers { | |
-webkit-touch-callout: none; | |
-webkit-user-select: none; | |
-khtml-user-select: none; | |
-moz-user-select: none; | |
-ms-user-select: none; | |
user-select: none; | |
text-align: right; | |
color: #ccc; | |
border-right: 1px solid #CCC; | |
vertical-align: top; | |
padding-left: 5px !important; | |
padding-right: 5px !important; | |
} | |
/* for block of code */ | |
.hljs-ln-code { | |
padding-left: 10px !important; | |
white-space: pre-wrap; | |
} | |
</style> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const TABLE_NAME = 'hljs-ln'; | |
const LINE_NAME = 'hljs-ln-line'; | |
const CODE_BLOCK_NAME = 'hljs-ln-code'; | |
const NUMBERS_BLOCK_NAME = 'hljs-ln-numbers'; | |
const NUMBER_LINE_NAME = 'hljs-ln-n'; | |
const DATA_ATTR_NAME = 'data-line-number'; | |
const BREAK_LINE_REGEXP = /\r\n|\r|\n/g; | |
function getLines(text: string) { | |
if (text.length === 0) return []; | |
return text.split(BREAK_LINE_REGEXP); | |
} | |
function addLineNumbersBlockFor(inputHtml: string, firstLineIndex: number) { | |
const lines = getLines(inputHtml); | |
// if last line contains only carriage return remove it | |
if (lines[lines.length - 1].trim() === '') { | |
lines.pop(); | |
} | |
if (lines.length > firstLineIndex) { | |
let html = ''; | |
lines.forEach((line, i) => { | |
i += 1; | |
html += ` | |
<tr> | |
<td class="${LINE_NAME} ${NUMBERS_BLOCK_NAME}" ${DATA_ATTR_NAME}="${i}"> | |
<div class="${NUMBER_LINE_NAME}" ${DATA_ATTR_NAME}="${i}"></div> | |
</td> | |
<td class="${LINE_NAME} ${CODE_BLOCK_NAME}" ${DATA_ATTR_NAME}="${i}"> | |
${line} | |
</td> | |
</tr>`.replace(/\n/g, ''); | |
}); | |
return `<table class="${TABLE_NAME}">${html}</table>`; | |
} | |
return inputHtml; | |
} | |
function lineNumbersInternal(element: HTMLElement, options: Options) { | |
// define options or set default | |
options = { | |
singleLine: false, | |
...options, | |
}; | |
// convert options | |
const firstLineIndex = options.singleLine ? 0 : 1; | |
return addLineNumbersBlockFor(element.innerHTML, firstLineIndex); | |
} | |
export interface Options { | |
singleLine?: boolean; | |
} | |
export default function lineNumbersValue(value: string, options: Options = {}) { | |
const element = document.createElement('code'); | |
element.innerHTML = value; | |
return lineNumbersInternal(element, options); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment