koca / vue-prism-editor

A dead simple code editor with syntax highlighting and line numbers. 3kb/gz
https://prism-editor.netlify.com
MIT License
751 stars 84 forks source link

Support for line highlighting #122

Closed viniciusccarvalho closed 2 years ago

viniciusccarvalho commented 3 years ago

Hi there, thanks for putting this up, pretty easy and amazing syntax highlighter for vue.

Is there a way to highlight a part of the editor, much like how github does when you pass a line information on viewing files?

Cheers

koca commented 2 years ago

Hey @viniciusccarvalho! i dont have any plans to add it to core however, there is a way to higlight some part of the editor since you control the higlighter function. If you want to see an example please checkout this repo : https://github.com/evwt/vue-tut

He also provided 2 live examples: scroll down to see highlighted code on the right.

Let me know if you need anything else :) Cheers!

Jchou24 commented 1 year ago

Hi, refer to the vue-tut. I make a component to highlight line numbers. Hope the code below will be useful.

<template>
    <v-card class="CodeDisplayer" rounded="lg">
        <PrismEditor
            class="CodeDisplayer-editor"
            v-model="modelValueComputed"
            :highlight="highlighter"
            :readonly="readonly"
            :tabSize="4"
            line-numbers
            ref="editor"
        />

        <CopyButton :copy-content="modelValueComputed" class="CopyButton" />
    </v-card>
</template>

<script setup lang="ts">
    import { VCard } from 'vuetify/components';
    import CopyButton from '../CopyButton/CopyButton.vue';

    import { computed, onMounted, ref } from 'vue';

    import { PrismEditor } from 'vue-prism-editor';
    import 'vue-prism-editor/dist/prismeditor.min.css'; // import the styles somewhere

    // import highlighting library (you can use any library you want just return html string)
    import prismjs from 'prismjs';
    // import { highlight, languages } from 'prismjs/components/prism-core';
    import 'prismjs/themes/prism-tomorrow.css'; // import syntax highlighting styles

    import 'prismjs/components/prism-clike';
    import 'prismjs/components/prism-javascript';
    import 'prismjs/components/prism-typescript';
    import 'prismjs/components/prism-csharp';
    import 'prismjs/components/prism-python';

    interface IProps {
        modelValue: string;
        grammar: prismjs.Grammar;
        language: string;
        readonly?: boolean;
        highlightLines?: Array<string | number>;
    }

    const props = withDefaults(defineProps<IProps>(), {
        modelValue: '',
        readonly: true,
        highlightLines: () => [],
    });

    const emit = defineEmits(['update:modelValue']);

    const modelValueComputed = computed({
        get: () => props.modelValue,
        set: (value) => emit('update:modelValue', value),
    });

    const highlighter = (code: string) => prismjs.highlight(code, props.grammar, props.language); // languages.<insert language> to return html with markup

    // =================================================================================
    // for line highlight

    const editor = ref(undefined);
    const $el = computed(() => (editor.value as any)?.$el as HTMLElement);
    const GetLineEl = (lineNumber: number) =>
        $el.value.querySelector(`.prism-editor__line-number:nth-child(${lineNumber + 1})`);

    const Highlight = () => {
        for (const highlightLine of props.highlightLines) {
            if (typeof highlightLine === 'string') {
                try {
                    const parse = (highlightLine || '').split('-').map((m) => parseInt(m));
                    HighlightLineRange(parse[0], parse[1]);
                } catch (error) {
                    console.log('[Jctk.Vue3.Ext/CodeDisplayer] Invalid highlight range. Format should be start-end');
                }
            } else if (Number.isInteger(highlightLine)) {
                HighlightLineNumber(highlightLine);
            }
        }
    };

    const HighlightLineNumber = (number: number) => {
        let line = GetLineEl(number);
        if (!line) {
            return;
        }
        line.classList.add('highlight-line');
    };

    const HighlightLineRange = (start: number, end: number) => {
        for (let idx = start; idx <= end; idx++) {
            HighlightLineNumber(idx);
        }
    };

    onMounted(() => {
        Highlight();
    });
    // =================================================================================
</script>

<style scoped lang="scss">
    .CodeDisplayer {
        position: relative;

        .CodeDisplayer-editor {
            /* we dont use `language-` classes anymore so thats why we need to add background and text color manually */
            background: #2d2d2d;
            color: #ccc;

            /* you must provide font-family font-size line-height. Example: */
            font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
            font-size: 14px;
            line-height: 1.5;
            padding-top: 12px;
            padding-bottom: 12px;
        }

        .CopyButton {
            position: absolute;
            right: 0;
            top: 0;
            z-index: 1;
        }
    }
</style>

<style lang="scss">
    // @import '../css/scrollbar.scss';
    @import 'jctk.vue3.ext/src/css/scrollbar.scss';

    /* required class */
    .CodeDisplayer {
        /* optional class for removing the outline */
        .prism-editor__textarea:focus {
            outline: none;
        }

        .prism-editor__editor {
            white-space: pre !important;
        }
        .prism-editor__container {
            overflow-x: scroll !important;
            padding-bottom: 4px;
            @include Scrollbar(14px, 8px);
        }

        // =================================================================================
        // for line highlight

        // $highlight-color-border: gray;
        // $highlight-color: rgba(128, 128, 128, 0.384);

        // $highlight-color-border: #42b983;
        // $highlight-color: rgba(66, 185, 131, 0.165);

        $highlight-color-border: #4e75a2;
        $highlight-color: rgba(1, 115, 254, 0.22);

        .prism-editor-wrapper {
            overflow: hidden;
        }

        .prism-editor__line-numbers {
            overflow: visible;
        }

        .prism-editor__line-number {
            border-left: 5px solid transparent;
            padding-left: 10px;
            position: relative;

            &.highlight-line {
                border-left: 5px solid $highlight-color-border;
                background: $highlight-color;
            }

            &.highlight-line:after {
                content: '';
                height: 21px;
                background: $highlight-color;
                pointer-events: none;
                position: absolute;
                z-index: 1;
                width: 100vw;
            }
        }
        // =================================================================================
    }
</style>