LoginSignup
0
0

More than 3 years have passed since last update.

highlight.js + Vue で行番号(line-numbers)を加えてhilight表示する。

Last updated at Posted at 2021-04-14

スクリーンショット 2021-04-15 1.11.31.png

hilight + 行番号を表示したものをゴールとして目指します。
参考にした記事、コードは下記です
highlight.jsで行ごとに区切りつつハイライトする
highlightjs/vue-plugin

行番号 + ハイライト を行う関数を作る

highlight.jsで行ごとに区切りつつハイライトする
を参考にすると、
単純に行区切りにhilightを適用しても、うまくいかないことがわかります。
そこで

let state = null
const result = hljs.highlight('markdown', row, true, state)
state = result.top

とし、
result.top
を継承することで行違いのhilightを働かせることができます。
これを利用し、

lineByLineHighilght
function lineByLineHighilght (language, body) {
  let state = null
  const output = []
  const bodySplit = body.split('\n')
  let maxLength = String(bodySplit.length).length
  for (let line = 0; line < bodySplit.length; line++) {
    const row = bodySplit[line]
    const result = hljs.highlight(language, row, true, state)
    let setLineNumber = `<div style="float: left; width: ${maxLength}7px;"><span style="float: right; padding-right: 5px;">${line}:</span></div>`
    let setLine = `<div class="lineNumber">${setLineNumber}<span class="line-value">${result.value}</span></div>`
    if (result.value.length === 0) {
      setLine = `<div class="lineNumber">${setLineNumber}</div></br>`
    }
    state = result.top
    output.push(setLine)
  }
  return output.join('')
}

とすることで、行番号を付与することができます。
ちなみに、

    if (result.value.length === 0) {
      setLine = `<div class="lineNumber">${setLineNumber}</div></br>`
    }

の部分ですが、これはresult.value.length === 0の時、改行されない問題が合ったので行ってます。
( もっと上手い解決策があれば教えてください:) )

VueJs上でhilight + 行番号表示を行う

vueのコンポーネントとして、hilightJsを扱えれば一番良く、
また公式側でvueでhilight.jsを扱えるようになっています。
hilight.js
ただ、今回はこのままでは扱えないので、
highlightjs/vue-pluginの中身を参考にし、

hilightVue.js

import hljs from 'highlight.js'

function hasValueOrEmptyAttribute (value) {
  return Boolean(value || value === '')
}

function lineByLineHighilght (language, body) {
  let state = null
  const output = []
  const bodySplit = body.split('\n')
  let maxLength = String(bodySplit.length).length
  for (let line = 0; line < bodySplit.length; line++) {
    const row = bodySplit[line]
    const result = hljs.highlight(language, row, true, state)
    let setLineNumber = `<div style="float: left; width: ${maxLength}7px;"><span style="float: right; padding-right: 5px;">${line}:</span></div>`
    let setLine = `<div class="lineNumber">${setLineNumber}<span class="line-value">${result.value}</span></div>`
    if (result.value.length === 0) {
      setLine = `<div class="lineNumber">${setLineNumber}</div></br>`
    }
    state = result.top
    output.push(setLine)
  }
  return output.join('')
}

const Component = {
  props: ['language', 'code'],
  data: function () {
    return {
      detectedLanguage: '',
      unknownLanguage: false
    }
  },
  computed: {
    className () {
      if (this.unknownLanguage) return ''

      return 'hljs ' + this.detectedLanguage
    },
    highlighted () {
      return lineByLineHighilght(this.language, this.code)
    },
    ignoreIllegals () {
      return true
    }
  },
  render (createElement) {
    return createElement('pre', {}, [
      createElement('code', {
        class: this.className,
        domProps: { innerHTML: this.highlighted }
      })
    ])
  }
}

export default {
  install (Vue) {
    Vue.component('highlightjs', Component)
  },
  component: Component
}


このように行うことで、任意なvueファイルで

<highlightjs language="HTML" :code="code"/>

と、
その任意のvueファイルが扱われる以前にrenderされているか、もしくは
その任意のファイル自身で

import hljs from '@/plugins/highlightVue.js'
Vue.use(hljs)

とすると、
下記のような結果にhilight + 行番号表示が行えます

スクリーンショット 2021-04-15 1.11.31.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0