What is meta? ×
Meta Stack Exchange is where users like you discuss bugs, features, and support issues that affect the software powering all 131 Stack Exchange communities.

This question already has an answer here:

Currently, the following code:

<!-- language: lang-php -->

    <?php="test"?>

<!-- language: lang-xml -->

    <bug cat="md" name="syntax">

Generates the following markdown:

<?php="test"?>
<bug cat="md" name="syntax">

Remembering the exact syntax to use when I actually have to use this isn't easy though. Is there an elegant way to add a button to the WYSIWYG editor, maybe with a dropdown for language choice?

share|improve this question

marked as duplicate by ben is uǝq backwards, hims056, Martijn Pieters, Aziz Shaikh, Richard Tingle Oct 5 '13 at 12:48

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

9  
The one that people should use more but don’t is <!-- language: none -->. Without it, random bits of data look really awful. All I can imagine is that folks don’t realize it exists. –  tchrist Feb 15 '13 at 2:41
2  
Perhaps the default behavior of the {} button could insert <!-- language: none --> which would both solve the "random bits" problem and remind people of the feature. –  DocSalvager Feb 21 '13 at 4:39
2  
@tchrist, while I knew about <!-- language: .... -->, I actually had no idea that "none" was a valid option (if I had thought about it, I probably would have guessed it, but it didn't even occur to me). So I think your hypothesis is correct that folks just don't realize it exists –  Ben Lee Feb 21 '13 at 20:15
    
Just noting I have not +1ed the question or either existing answer because they do not cater for the fact that the current approach of the question's main tag determining the language formatting used works well enough that these hints are only needed occasionally. Of course, when they are need I'd like to easily recall the syntax and available languages too. I've favourited the relevant meta faq/detailed answer that does record these details. –  Mark Hurd May 17 '13 at 7:41
    
@MarkHurd How would one "cater for this fact"? –  Asad Jun 10 '13 at 5:00
    
@Asad Both current answers now cater for this, by only displaying when needed. –  Mark Hurd Jun 11 '13 at 12:16
    
@MarkHurd One case where this isn't true is VB.Net/C# - 99 times out of 100, it doesn't matter if the answer is in either language as it's very simple to convert between them. Because of this, I tend to simply tag .Net - which results in C# highlighting of VB.Net code (works fine until you get to a code comment ' which is interpreted as a string start) –  Basic Oct 18 '13 at 9:12

2 Answers 2

What I think SO needs is:

  • A button next to the a button on the editor toolbar.

Clicking the button would insert:

<!-- language: x -->

The caret would be automatically placed where the x is above, and a JS-driven Intellisense-style dropdown list would populate with some choices, something like this:

a

Typing lang-c would present:

a

My 2¢.

share|improve this answer
2  
Alternatively, a drop down with language selection next to the {} button. –  Richard Feb 22 '13 at 9:30

What it is:

I've worked the kinks out of an implementation of something similar to the suggestion above, which you can test out here (doesn't seem to be working in Chrome, for some reason). It differs in that there is no separate button: once you create a code block, a dropdown appears, giving you the option to add a language hint tag.

The code inside is still pretty horrible, (eg. using jQuery inside code with no jQuery dependency), but it can probably be refactored into something nicer with a little more work.

Screenshots:

enter image description here

enter image description here

How to use:

Adding hints:

  • Select some text and use Ctrl+K or the {} button as normal
  • A dropdown will appear above the newly indented code that you can use to select a language
  • You can either select a language using the dropdown, or click anywhere else to ignore it
  • Note that any existing language hint tags within your selection will be removed. For example, consider the following markdown:

    This is some sample text.
    
    This part is non programming language data.
    

    Select the second line and press Ctrl+K, then select "None" from the dropdown:

    This is some sample text.
    
    <!-- language: lang-none-->
    
        This part is non programming language data.
    

    If you were to now select the entirety of this and press Ctrl+K, then select "Default" in the dropdown, the output would be:

    <!-- language: lang-default-->
    
        This is some sample text.
    
            This part is non programming language data.
    

Removing hints:

  • Select some indented code, including the language hint tag at the beginning
  • Use Ctrl+K or the {} button to dedent code into normal text
  • The language hint is removed

Help please:

If anyone more familiar with the PageDown source can refactor the code, add more features, fix bugs etc., feel free to do so. I've set up a Github repo here.

The only thing I modified was the doCode method of the CommandManager prototype in Markdown.Editor.js:

commandProto.doCode = function (chunk, postProcessing) {

    //Test if there is text preceding the start of the selection on the first selected line
    //or if there is text following the end of the selection on the last selected line
    var hasTextBefore = /\S[ ]*$/.test(chunk.before);
    var hasTextAfter = /^[ ]*\S/.test(chunk.after);

    // Use 'four space' markdown if the selection is on its own
    // line or is multiline.
    if ((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)) {

        chunk.before = chunk.before.replace(/[ ]{4}$/,
            function (totalMatch) {
                chunk.selection = totalMatch + chunk.selection;
                return "";
            });

        var nLinesBack = 1;
        var nLinesForward = 1;

        if (/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)) {
            nLinesBack = 0;
        }
        if (/^\n(\t|[ ]{4,})/.test(chunk.after)) {
            nLinesForward = 0;
        }

        chunk.skipLines(nLinesBack, nLinesForward);

        var langPos = chunk.before.length;

        //Retrieve textarea and define dropdown creator
        var txt = this.panels.input;
        function dropdown(){
            var pos,
            langs = {
                "None":"lang-none",
                "Default":"lang-default",
                "Bash":"lang-bsh",
                "C":"lang-c",
                "C++":"lang-cpp",
                "C#":"lang-cs",
                "CSH? I don't know":"lang-csh",
                "CSS":"lang-css",
                "Haskell":"lang-hs",
                "HTML":"lang-html",
                "Java":"lang-java",
                "JavaScript":"lang-js",
                "Lisp":"lang-lisp",
                "Lua":"lang-lua",
                "F#":"lang-ml",
                "Perl":"lang-perl",
                "PHP":"lang-php",
                "Python":"lang-py",
                "Proto":"lang-proto",
                "Ruby":"lang-rb",
                "Scala":"lang-scala",
                "Shell":"lang-sh",
                "SQL":"lang-sql",
                "Visual Basic":"lang-vb",
                "XML":"lang-xml"
            };

            //Use plugin to find coordinates of caret
            $(txt).textareaHelper();
            pos = $(txt).textareaHelper('caretPos');
            $(txt).textareaHelper('destroy');

            //Account for offset of textarea
            pos.top += $(txt).offset().top
            pos.left = $(txt).offset().left
            console.log(pos);

            //Populate dropdown with options
            var langpicker = $('<select>');
            langpicker.append(function(){
                var option = $("<option>")
                            .prop({value:""})
                            .html("Select a language");
                return option;
            })
            $.each(langs, function(i, v){
                var option = $("<option>")
                            .prop({value:"<!-- language: " + v + "-->"})
                            .html(i);
                langpicker.append(option);
            });

            //Position dropdown
            langpicker.appendTo($('body')).css({
                position: 'absolute', 
                left: pos.left + 2, 
                top: pos.top - langpicker.outerHeight(),
                width: "200px"
            })
            .hide()
            .fadeIn()
            .focus()
            .on('change blur', select);

            //Callback function
            function select(e){
                var that = this;
                if(e.type == "change"){
                    $(txt).val(function(i, v){
                        console.log(langPos, v.slice(langPos));
                        return v.slice(0, Math.max(langPos,0)) + $(that).val() + "\n\n" + v.slice(Math.max(langPos,0));
                    });
                }
                $(this).fadeOut(function(){
                    $(this).remove();
                });
            }
        }

        if (!chunk.selection) {
            chunk.startTag = "    ";
            chunk.selection = this.getString("codeexample");
        }
        else {
            //If the block isn't already code, add 4 spaces to the beginning of each line
            if (/^(?![ ]*<\!-- language: lang-[a-z]+-->)[ ]{0,3}\S/m.test(chunk.selection)) {
                if (/\n/.test(chunk.selection))
                    chunk.selection = chunk.selection.replace(/^[ ]{0,3}<\!-- language: lang-[a-z]+-->\n{0,2}/gm, "").replace(/^/gm, "    ");
                else // if it's not multiline, do not select the four added spaces; this is more consistent with the doList behavior
                    chunk.before += "    ";

                txt.setSelectionRange(langPos, langPos);
                setTimeout(dropdown, 0);
            }
            //If the block already is code, remove spaces from the beginning of each line
            else {
                chunk.selection = chunk.selection.replace(/(?:(\n)?[ ]{4}|[ ]{0,3}\t)|(?:^<\!-- language: lang-[a-z]+-->\n*)/g, "$1");
            }
        }

    }
    else {
        // Use backticks (`) to delimit the code block.

        chunk.trimWhitespace();
        chunk.findTags(/`/, /`/);

        if (!chunk.startTag && !chunk.endTag) {
            chunk.startTag = chunk.endTag = "`";
            if (!chunk.selection) {
                chunk.selection = this.getString("codeexample");
            }
        }
        else if (chunk.endTag && !chunk.startTag) {
            chunk.before += chunk.endTag;
            chunk.endTag = "";
        }
        else {
            chunk.startTag = chunk.endTag = "";
        }
    }
};
share|improve this answer
    
IMO, this is better than my answer. –  Danny Beckett Feb 24 '13 at 2:48
    
@DannyBeckett This one does look fancy, but it comes at the expense of ease of use; the dropdown popping up out of nowhere might be confusing to some people. I think a button in the tools menu is a good idea. –  Asad Feb 24 '13 at 4:30
    
If each of the languages starts with lang-, then there is no need to define it in each of the languages. You currently have this .prop({value:""}), where you could turn it into .prop({"lang-" + value:""}) –  Cody Guldner May 29 '13 at 5:23
    
@CodyGuldner Good point. You can edit the answer to improve it. –  Asad May 29 '13 at 13:58
    
Is there a bug? I have to press that {} button or Ctrl+K 3 times in order to see the drop-down menu. –  Antony Jun 10 '13 at 4:48
    
@Antony In Firefox? –  Asad Jun 10 '13 at 4:49
    
@Asad Yes, as you said it doesn't work on Chrome. The same thing happens on Safari. –  Antony Jun 10 '13 at 4:49
    
@Antony I'll try debugging this now. –  Asad Jun 10 '13 at 4:51
    
@Antony I'm getting a bunch of Refused to execute script from [script URI] because its MIME type ('text/plain') is not executable. That's what happens when you try to use GitHub as a CDN I guess. –  Asad Jun 10 '13 at 4:53
    
@Asad I guess there isn't a bug. It's just that I used it wrongly. But maybe you can make it work when someone just start by pressing the {} button without writing any codes. –  Antony Jun 10 '13 at 4:53
    
@Asad You can use the gh-pages to serve js to avoid this error. –  Antony Jun 10 '13 at 4:54

Not the answer you're looking for? Browse other questions tagged .