Skip to content

Instantly share code, notes, and snippets.

@lights0123
Last active August 17, 2020 06:00
Show Gist options
  • Save lights0123/51c9eb04d3de80529b01741ec4137bd7 to your computer and use it in GitHub Desktop.
Save lights0123/51c9eb04d3de80529b01741ec4137bd7 to your computer and use it in GitHub Desktop.
vue-highlight with line numbers
<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>
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