microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
162.67k stars 28.68k forks source link

[html] Syntax Highlighting is way off with new line after event handler #162277

Closed davidgs closed 1 year ago

davidgs commented 1 year ago

Type: Bug

In the latest release syntax highlighting in html files with included JavaScript is so far off it's unusable.

I have a comment (which should be green)

// not sure yet

Syntax highlighting has the // not the correct color. su is dark blue. re is green again. yet. is a different color blue. The color choices and what parts of words are highlighted seems random.

VS Code version: Code 1.71.2 (74b1f979648cc44d385a2286793c226e611f59e7, 2022-09-14T21:07:15.900Z) OS version: Darwin arm64 21.6.0 Modes: Sandboxed: No

System Info |Item|Value| |---|---| |CPUs|Apple M1 Pro (8 x 24)| |GPU Status|2d_canvas: enabled
canvas_oop_rasterization: disabled_off
direct_rendering_display_compositor: disabled_off_ok
gpu_compositing: enabled
metal: disabled_off
multiple_raster_threads: enabled_on
opengl: enabled_on
rasterization: enabled
raw_draw: disabled_off_ok
skia_renderer: enabled_on
video_decode: enabled
video_encode: enabled
vulkan: disabled_off
webgl: enabled
webgl2: enabled
webgpu: disabled_off| |Load (avg)|7, 6, 6| |Memory (System)|32.00GB (0.07GB free)| |Process Argv|--crash-reporter-id cf7ba3b3-413c-48e5-8285-9982b42776fa| |Screen Reader|no| |VM|0%|
Extensions (105) Extension|Author (truncated)|Version ---|---|--- html-snippets|abu|0.2.1 vscode-languagetool|ada|3.8.0 vscode-languagetool-en|ada|3.8.0 vscode-icalendar|af4|1.0.1 vscode-color|ans|0.4.5 code-gnu-global|aus|0.2.2 simple-react-snippets|bur|1.2.7 vscode-reveal-hugo|cha|0.2.5 doxdocgen|csc|1.4.0 vscode-postgresql-client2|cwe|5.7.11 vscode-mac-color-picker|dae|1.1.0 vscode-eslint|dba|2.2.6 vscode-instant-markdown|dba|1.4.7 vscode-springboot-snippets|dev|1.2.0 xml|Dot|2.5.1 rust-syntax|dus|0.6.1 vscode-html-css|ecm|1.13.1 EditorConfig|Edi|0.16.4 vscode-npm-script|eg2|0.3.28 esp-idf-extension|esp|1.5.0 vscode-diff|fab|1.4.2 markdown-table-formatter|fcr|2.2.4 change-case|Fin|0.0.6 vs-code-http-server-and-html-preview|Fli|2.2.2 copilot|Git|1.47.6882 go|gol|0.35.2 CppSnippets|har|0.0.15 rest-client|hum|0.25.1 flux|inf|1.0.4 mediawiki|jas|0.0.4 tablegenerator|Jay|1.0.4 better-cpp-syntax|jef|1.15.19 react-component|jer|1.1.0 vscode-hacker-typer|jev|0.1.1 diff-tool|jin|0.0.1 hugo-shortcode-syntax|kae|1.0.0 vscode-github|Kni|0.30.7 cortex-debug|mar|1.6.5 vscode-language-babel|mgm|0.0.36 vscode-clang|mit|0.2.4 vscode-http-client|mkl|0.34.0 prettify-json|moh|0.0.3 mongodb-vscode|mon|0.9.3 mongoose-os-ide|mon|0.6.0 vscode-docker|ms-|1.22.1 csharp|ms-|1.25.0 data-workspace-vscode|ms-|0.3.0 mssql|ms-|1.16.0 sql-bindings-vscode|ms-|0.3.0 sql-database-projects-vscode|ms-|0.19.0 python|ms-|2022.14.0 vscode-pylance|ms-|2022.9.40 jupyter|ms-|2022.8.1002431955 jupyter-keymap|ms-|1.0.0 jupyter-renderers|ms-|1.0.9 remote-containers|ms-|0.251.0 remote-ssh|ms-|0.84.0 remote-ssh-edit|ms-|0.82.0 remote-wsl|ms-|0.66.3 vscode-remote-extensionpack|ms-|0.21.0 cmake-tools|ms-|1.12.27 cpptools|ms-|1.12.4 cpptools-extension-pack|ms-|1.3.0 live-server|ms-|0.4.1 vsliveshare-pack|ms-|0.4.0 vscode-react-native|msj|1.9.3 sqltools|mtx|0.25.1 sqltools-driver-pg|mtx|0.3.0 vetur|oct|0.36.0 particle-vscode-core|par|1.15.4 particle-vscode-pack|par|1.15.4 particle-vscode-snippets|par|1.15.4 vscode-boot-dev-pack|Piv|0.1.0 vscode-concourse|Piv|1.39.0 vscode-manifest-yaml|Piv|1.39.0 vscode-spring-boot|Piv|1.39.0 platformio-ide|pla|2.5.4 r-debugger|RDe|0.4.7 java|red|1.10.0 r|REd|2.5.3 wordcount|rid|0.1.33 bash-debug|rog|0.3.9 rust-analyzer|rus|0.3.1221 sockscode-vscode|shy|1.0.1 stackery|sta|1.1.4 spellchecker|swy|1.4.0 markdowntable|Tak|0.10.2 html-preview-vscode|tht|0.2.5 cmake|twx|0.0.17 vscode-arduino|vsc|0.4.12 vscode-java-debug|vsc|0.44.0 vscode-java-dependency|vsc|0.21.0 vscode-java-pack|vsc|0.25.2 vscode-java-test|vsc|0.37.1 vscode-maven|vsc|0.39.0 vscode-spring-boot-dashboard|vsc|0.7.1 vscode-spring-initializr|vsc|0.11.0 codetour|vsl|0.0.58 gistfs|vsl|0.4.1 vscode-wakatime|Wak|19.3.0 full-react-snippets|wal|1.4.2 vscode-java-saber|You|0.1.2 markdown-all-in-one|yzh|3.4.3 vscode-aspell|zap|0.1.4 propertylist|zho|0.0.2 (2 theme extensions excluded)
A/B Experiments ``` vsliv368cf:30146710 vsreu685:30147344 python383cf:30185419 vspor879:30202332 vspor708:30202333 vspor363:30204092 vswsl492cf:30256860 vslsvsres303:30308271 pythonvspyl392:30443607 vserr242:30382549 pythontb:30283811 vsjup518:30340749 pythonptprofiler:30281270 vshan820:30294714 vstes263:30335439 vscorecescf:30445987 pythondataviewer:30285071 vscod805:30301674 binariesv615:30325510 bridge0708:30335490 bridge0723:30353136 cmake_vspar411cf:30557515 vsaa593cf:30376535 pythonvs932:30410667 cppdebug:30492333 vscaat:30438848 vsclangdc:30486549 c4g48928:30535728 hb751961:30553087 dsvsc012:30540252 azure-dev_surveyone:30548225 2144e591:30553903 40g7c324:30573242 ```
davidgs commented 1 year ago
Screen Shot 2022-09-28 at 6 56 06 PM Screen Shot 2022-09-28 at 6 15 54 AM
mjbvz commented 1 year ago

Does this reproduce in the latest VS Code insiders build with all extensions disabled?

davidgs commented 1 year ago

It sure does.

Screen Shot 2022-09-28 at 7 12 08 PM Screen Shot 2022-09-28 at 7 13 25 PM
mjbvz commented 1 year ago

Please share the text of file that causes this

davidgs commented 1 year ago

It ain't pretty ...

<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">
<style>
  .custom-file-input.selected:lang(en)::after {
    content: "" !important;
  }

  .custom-file {
    overflow: hidden;
  }

  .custom-file-input {
    white-space: nowrap;
  }

  .pill {
    font-size: 12px;
    font-family: "Readex Pro", sans-serif;
    padding: 5px 10px;
    padding-right: 25px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    margin-right: 5px;
    margin-left: -1px;
    margin-bottom: 5px;
    border-radius: 100px;
    font-size: 14px;
    border: none;
    outline: none;
    background: #dddddd;
    cursor: pointer;
  }

  .pill:not(.pill--selected):hover {
    background: #cccccc;
  }

  .pill--selected {
    background: #009578;
    color: #ffffff;
  }

  .pill--purple {
    font-size: 12px;
    font-family: "Readex Pro", sans-serif;
    padding: 5px 10px;
    padding-right: 25px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    margin-right: 5px;
    margin-left: -1px;
    margin-bottom: 5px;
    border-radius: 100px;
    font-size: 14px;
    border: none;
    outline: none;
    cursor: pointer;
    background: linear-gradient(135deg, #4235d0 38%, #f969cd 100%);
    color: #ffffff;
  }

  .form-label-pos {
    position: absolute;
    margin-left: 20px;
  }

  .image-div-format {
    width: 200px !important;
    padding-top: 25px !important;
  }

  .close-pos {
    margin-top: 2px;
    position: absolute;
    margin-left: 175px;
  }

  .upload-image {
    width: 30%;
    display: inline-block;
  }

  .use-pos {
    margin-top: 2px;
    position: absolute;
    margin-right: 2px;
  }

  .close {
    position: relative;
    right: 10px;
    top: 7px;
    width: 6px;
    height: 6px;
    opacity: 1;
    z-index: 99;
    cursor: pointer;
  }

  .close:hover {
    opacity: 0.3;
  }

  .close:before,
  .close:after {
    position: absolute;
    left: 20px;
    content: ' ';
    height: 8px;
    width: 2px;
    background-color: #ffffff;
    cursor: pointer;
  }

  .close:before {
    transform: rotate(45deg);
  }

  .close:after {
    transform: rotate(-45deg);
  }
</style>
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"
  integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js"
  integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>

<h2><span class='text-span'>Time</span> to tell your story</h2>
<p><span class='text-span'>Use</span> the form below to create a new story. You can use the editor to format your story
  and add images.</p>
<p><span class='text-span'>These</span> first few fields are mandatory so please fill them out completely.</p>
<form id="storyForm" name="storyForm" class="row g-3 needs-validation" novalidate>
  <div class="form-floating col-md-6" data-bs-toggle="This will also be the 'slug' for your post">
    <input type="text" class="form-control" id="titleInput" placeholder="The title of your story"
      aria-describedby="titleHelpBlock" required oninput=enableDesc(this)
      data-bs-toggle="This will also be the 'slug' for your post"></input>
    <label for="titleInput" data-bs-toggle="This will also be the 'slug' for your post">Title
    </label>
    <div id="titleHelpBlock" class="form-text">
      Give your story a compelling title. This field is required.
    </div>
  </div>
  <div class="form-floating col-md-6">
    <input type="text" class="form-control" id="descriptionInput" placeholder="Describe your story"
      aria-describedby="descriptionHelpBlock" required disabled oninput=enableDesc(this)>
    <label for="descriptionInput">Description
    </label>
    <div id="descriptionHelpBlock" class="form-text">
      Give your story a brief description. This field is required.
    </div>
    <div class="valid-feedback">
      Looks good!
    </div>
    <div class="invalid-feedback">
      You must provide a description for your story.
    </div>
  </div>
  <div class="form-floating col-md-12">
    <input type="text" class="form-control" id="nameInput" placeholder="Your name or pseudonym"
      aria-describedby="nameHelpBlock" required disabled oninput=enableDesc(this)>
    </input>
    <label for="nameInput">Name or Pseudonym</label>
    <div id="nameHelpBlock" class="form-text">
      You do <strong>not</strong> have to supply your real name. Please feel free to use an anonymous pseudonym if you
      wish.
    </div>
    <div class="invalid-feedback">
      You must provide either your real name or a pseudonym before submitting.
    </div>
    <div class="valid-feedback">
      Looks good!
    </div>
  </div>
  <div class="input-group input-group-lg col-md-12">
    <input type="file" class="form-control" id="customFileInput" placeholder="Choose your Header Image"
      aria-describedby="imageHelpBlock" required onchange=setImage() disabled oninput=enableDesc(this)>
    </input>
    <div id="imageHelpBlock" class="form-text">
      Choose an image you'd like to use as the header-image for your story. (We use <a href="https://unsplash.com/"
        target="_blank">Unsplash</a> for our images.)
    </div>
    <div class="invalid-feedback">
      You must provide a header image for your story.
    </div>
    <div class="valid-feedback">
      Looks good!
    </div>
  </div>
  <div id="image" class="col-md-12"></div>
  <div class="form-floating col-md-12">
    <input type="text" class="form-control" id="tagsInput" placeholder="Tags" aria-describedby="tagsHelpBlock"
      oninput=addPill() disabled oninput=enableDesc(this)>
    </input>
    <label for="tagsInput">Tags</label -->
    <div id="tagsHelpBlock" class="form-text">
      Enter a comma-separated list of tags for your story. These will be used to help people find your story.
    </div>
    <div class="valid-feedback">
      Looks good!
    </div>
  </div>
  <div class="form-floating col-md-12" id="tags-div">
  </div>
  <h2><span class='text-span'>Your</span> story</h2>
  <div class="form-floating col-md-12">
    <textarea id="mde-input" required disabled oninput=enableDesc(this)>
    </textarea>
    <div class="invalid-feedback">
      You must provide a story.
    </div>
    <div class="valid-feedback">
      Looks good!
    </div>
    <div id="storyHeaderHelpBlock" class="form-text">
      Write your story here in Markdown. The browser will autosave the contents for you so you don't have to worry about
      losing your work. When you're happy with your story, click the submit button.
    </div>
  </div>
  <div class="form-floating col-md-12">
    <span class='text-span'>The</span> following fields are <strong>not</strong> required.
  </div>
  <div class="form-floating col-md-6">
    <input type="email" class="form-control" id="emailInput" placeholder="name@example.com"
      aria-describedby="emailHelpBlock">
    </input>
    <label for="emailInput">Email address</label>
    <div id="emailHelpBlock" class="form-text">
      You do <strong>not</strong> have to supply your email address. If you do, we will never share it with anyone
      else.
    </div>
  </div>
  <div class="form-floating col-md-6">
    <label for="twitterUsername" class="form-label">Twitter Handle</label>
    <div class="input-group has-validation">
      <span class="input-group-text" id="inputGroupPrepend">@</span>
      <input type="text" class="form-control" id="twitterUsername" aria-describedby="twitterUsernameHelpBlock">
      </input>
      <div id="twitterUsernameHelpBlock" class="form-text">
        You do <strong>not</strong> have to supply your twitter handle. If you do, we will link your submission to
        your
        twitter account.
      </div>
    </div>
  </div>
  <div class="form-floating col-md-12">
    <input type="url" class="form-control" id="webInput" placeholder="Your website" aria-describedby="webHelpBlock">
    </input>
    <label for="webInput">Your website</label>
    <div id="webHelpBlock" class="form-text">
      You do <strong>not</strong> have to supply this. If you do, we will link your submission back to this address.
    </div>
  </div>
  <div class="d-grid gap-2 d-md-flex justify-content-md-end">
    <div class="form-floating col-md-8">
      <div class="form-check">
        <input class="form-check-input" type="checkbox" id="reverseCheck1" onchange=checkCOC()>
        </input>
        <label class="form-check-label" for="reverseCheck1">
          I have read and agree to the <a href="/code_of_conduct" target="_blank">Code of Conduct</a> and <a
            href="/privacy_policy" target="_blank">Privacy Policy</a>.
        </label>
        <div class="invalid-feedback">
          You must agree before submitting.
        </div>
        <div class="valid-feedback">
          Looks good!
        </div>
      </div>
    </div>
    <div class="form-floating col-md-2">
      <button id="submitButton" class="btn btn-primary me-md-2" type="submit" disabled>Submit</button>
    </div>
    <div class="form-floating col-md-2">
      <button class="btn btn-danger" type="button" onClick=clearForm()>Clear</button>
    </div>
  </div>
</form>
<script>

  // Tooltips -- don't work, look into why
  const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
  const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))

  // have they agreed to the CoC yet?
  let coc = false;

  // Set up the Markdown editor
  const easyMDE = new EasyMDE({
    autoDownloadFontAwesome: true,
    showIcons: [
      'strikethrough',
      'code',
      'table',
      'redo',
      'undo',
      'clean-block',
      'horizontal-rule'
    ],
    element: document.getElementById('mde-input'),
  });

  // not sure yet.
  const qae = document.querySelector('custom-file-input')

  addEventListener('change', function (e) {
    var name = document.getElementById("customFileInput").files[0].name;
    var nextSibling = e.target.nextElementSibling
    nextSibling.innerText = name
  })

  // Enable the next text field as soon as you've typed into this one.
  function enableDesc(e) {
    var inp = document.getElementsByTagName("input");
    var t = this.event.target.id
    for (let i = 0; i < inp.length; i++) {
      if (inp[i].id == t) {
        inp[i + 1].disabled = false;
        return
      }
    }
  }

  // upload an image
  function setImage() {
    const f = document.getElementById("customFileInput").files[0];
    console.log("setImage: " + document.getElementById("customFileInput").files[0]);
    const reader = new FileReader();
    reader.readAsDataURL(f);
    reader.onload = function () {
      var data = {
        "type": "image",
        "name": f.name,
        "image": `${reader.result}`,
        "title": `${document.getElementById("titleInput").value}`
      }
      fetch('http://localhost:9494/submitImage', {
        mode: 'cors',
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
      })
        .then(response => {
          if (response.status == 200) {
            console.log("Image uploaded successfully")
            response.json().then(data => {
              insertImage(data.location, reader);
              console.log(data);
            });
          }
        })
        .catch((error) => {
          console.error('Error:', error);
        });
    };
  }

  function insertImage(location, reader) {
    const d = document.getElementById("image")
    const div = document.createElement("div");
    div.setAttribute("class", "upload-image");
    div.setAttribute("id", "http://localhost/" + location)
    const cb = document.createElement("button");
    cb.setAttribute("type", "button");
    cb.setAttribute("class", "btn-close close-pos");
    cb.setAttribute("aria-label", "Close");
    cb.addEventListener("click", (e) => {
      const p = e.target.parentNode;
      p.remove();
    });
    const im = document.createElement("img")
    im.setAttribute("src", `${reader.result}`);
    const uc = document.createElement("button");
    uc.setAttribute("type", "checkbox");
    uc.checked = false;
    uc.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      var val = easyMDE.value();
      val += "\n![image](" + id + ")\n";
      easyMDE.value(val);
    });
    uc.setAttribute("class", "form-check-input use-pos");
    uc.setAttribute("id", "useImage");
    ucl = document.createElement("label");
    imgDiv = document.createElement("div");
    imgDiv.setAttribute("class", "image-div-format");
    ucl.setAttribute("class", "form-check-label form-label-pos");
    ucl.setAttribute("for", "useImage");
    ucl.innerText = "Use this image";
    div.appendChild(uc);
    div.appendChild(ucl);
    div.appendChild(cb);
    div.appendChild(imgDiv);
    imgDiv.appendChild(im);
    d.appendChild(div);
  }

  function addPill() {
    if !document.getElementById("tagsInput").value.includes(","){
      return
    }
    const tags = document.getElementById("tagsInput").value;
    console.log(document.getElementById("tagsInput").value);
    if (tags.includes(",")) {
      const tagsArray = tags.split(",");
      const tagsDiv = document.getElementById("tags-div");
      const pill = document.createElement("button");
      pill.classList.add("pill--purple");
      pill.setAttribute("type", "button");
      pill.innerText = tagsArray[0];
      pill.innerHTML += `<span class="close"></span>`;
      tagsDiv.appendChild(pill);
      pill.addEventListener("click", (e) => {
        const p = e.target.parentElement;
        e.currentTarget.remove();
      });
      document.getElementById("tagsInput").value = "";
    }
  }

  function clearForm() {
    easyMDE.value('');
  }
  function onSubmit() {
    console.log("submitting");
  }

  document.getElementById("storyForm").addEventListener("submit", async (e) => {
    e.preventDefault();
    const forms = document.querySelectorAll('.needs-validation')
    const title = document.getElementById("titleInput").value;
    const description = document.getElementById("descriptionInput").value;
    const image = document.getElementById("imageInput").value;
    const email = document.getElementById("emailInput").value;
    const twitter = document.getElementById("twitterUsername").value;
    const name = document.getElementById("nameInput").value;
    const web = document.getElementById("webInput").value;
    const content = easyMDE.value();
    const data = {
      title: title,
      description: description,
      image: image,
      email: email,
      twitter: twitter,
      name: name,
      web: web,
      content: content
    };
    console.log(data);
    fetch('https://localhost:9494/submitStory', { mode: 'cors', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) })
      .then(response => response.json())
      .then(data => {
        console.log('Success:', data);
      })
      .catch((error) => {
        console.error('Error:', error);
      });
  });

  function checkCOC() {
    coc = !coc;
    console.log(coc);
    if (coc) {
      document.getElementById("submitButton").removeAttribute("disabled");
    } else {
      document.getElementById("submitButton").setAttribute("disabled", "disabled");
    }
  }
</script>
davidgs commented 1 year ago

It's a template file for Hugo. Specifically a shortcode file called editor.html

RedCMD commented 1 year ago

same as https://github.com/microsoft/vscode/issues/161480

semantic highlighting is broken for some reason

aeschli commented 1 year ago

Reproduced with

<input oninput=enableDesc(this)
></input> 
<script>

  const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
  const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
</script>

The special thing that there's a new line right after enableDesc(this)