haskelly-dev / Haskelly

Haskelly is a VS Code extension that provides complete support for casual and expert Haskell development.
https://marketplace.visualstudio.com/items?itemName=UCL.haskelly
GNU General Public License v3.0
153 stars 17 forks source link

QuickCheck Not Working #81

Open bucklj4 opened 5 years ago

bucklj4 commented 5 years ago

Hi,

The QuickCheck button does not seem to be working as it should. In particular whenever I click it, I get the following VS error message: VS Code can't execute this file. Check the terminal. In the terminal, I see type c:\Users\%USER%\.vscode\extensions\ucl.haskelly-0.5.4\ad39fbca-d904-42a7-92bf-b7e66c5a895a.txt -------- Error -------- Error: ENOENT: no such file or directory, open 'c:\Users\%USER%\Documents\e49dcfb1-2bb0-4677-9758-d82bee8cb210.hs'------------------------

Maxez commented 5 years ago

I got annoyed by the QuickCheck button not working so I decided to fix it. Here is my solution (tested on Windows 10, fully modified file is attached at the end of the post):

  1. We need to modify testCode.js file in Haskelly installation directory (in my case the file was located at C:\Users\YourUsername\.vscode\extensions\ucl.haskelly-0.5.5\out\src\helpers\testCode.js). Open it in a text editor.
  2. There is a bug in testHaskellFile function. In case of a non-stack project we want to create a copy of the original haskell source code, remove main function from it and add code that runs the tests. The original code asynchronously creates the copy and reads from it. We want to start reading AFTER the copy is finished. Replace line 136 fs.createReadStream(filePath).pipe(fs.createWriteStream(newPath)); with this
    let writer = fs.createWriteStream(newPath);
    fs.createReadStream(filePath).pipe(writer);

    then place the reading code inside this

    writer.once('finish', () => {
    //here goes the reading
     fs.readFile(newPath...
    });

    This way we ensure that we read the file once we finished copying.

  3. There is also a problem with main function removal part. The original code expects the main function to be at the end of a source code file and that there will be no main function type declaration. Here is my improved version of removeMainFunction function:

    function removeMultilineComments(data) {
    return data.replace(/{-[\s\S]*?-}/g, (comment) => {
        return comment.replace(/\S/g, ' ');
    });
    }
    function removeMainFunction(data) {
    const main_decl_regex = /^main\s*::.*$/m;
    const main_def_regex = /^main\s*=.*$/m;
    const comment_regex = /^\s*--.*$/m;
    const other_regex = /^\S+.*$/m;
    const dataArray = removeMultilineComments(data).split('\n');
    let decl_start;
    let decl_end;
    let def_start;
    let def_end;
    for (let i = 0; i < dataArray.length; i++) {
        //main type signature
        if (main_decl_regex.test(dataArray[i])) {
            if (def_start !== undefined && def_end === undefined) {
                def_end = i;
            }
            if (decl_start === undefined) {
                decl_start = i;
            }
        }
        //main definition
        else if (main_def_regex.test(dataArray[i])) {
            if (decl_start !== undefined && decl_end === undefined) {
                decl_end = i;
            }
            if (def_start === undefined) {
                def_start = i;
            }
        }
        //skip comments
        else if (comment_regex.test(dataArray[i])) {
            //do nothing
        }
        //different expression/declaration
        else if (other_regex.test(dataArray[i])) {
            if (decl_start !== undefined && decl_end === undefined) {
                decl_end = i;
            }
            if (def_start !== undefined && def_end === undefined) {
                def_end = i;
            }
        }
    }
    if (decl_start !== undefined && decl_end === undefined) {
        decl_end = dataArray.length;
    }
    if (def_start !== undefined && def_end === undefined) {
        def_end = dataArray.length;
    }
    
    return dataArray.filter((value, index, arr) => {
        return (decl_start === undefined || index < decl_start || index >= decl_end) &&
               (def_start === undefined || index < def_start || index >= def_end);
    }).join('\n');
    }

    That's it. Worked for me. You can find modified testCode.js here (it's packed as .zip): testCode.zip

martrik commented 5 years ago

Hello @maxez, thanks a lot for your contribution! Would you mind opening a PR with your change so that your contribution is recorded and I can formally merge it? Thanks again!

Maxez commented 5 years ago

Hi @martrik. I created the PR with my changes (I modified testCode.ts file from which I guess the testCode.js is generated). Please verify it since I'm not a JS programmer.