diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..10b6ebb --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,125 @@ +version: 2 + +defaults: &defaults + working_directory: /tmp/project + docker: + - image: arcanemagus/atom-docker-ci:stable + steps: + # Restore project state + - attach_workspace: + at: /tmp + - run: + name: Install Julia v0.6.4 + command: | + curl -o julia.tar.gz --location --silent --show-error \ + https://julialang-s3.julialang.org/bin/linux/x64/0.6/julia-0.6.4-linux-x86_64.tar.gz && \ + mkdir julia && \ + tar --extract --gzip --strip 1 --directory=julia --file=julia.tar.gz && \ + echo 'export PATH="/tmp/project/julia/bin:$PATH"' >> $BASH_ENV + - run: + name: Julia version + command: julia --version + - run: + name: Install Lint.jl + # Note the "using Lint" is to pre-compile the cache + command: julia -E 'Pkg.add("Lint"); using Lint;' + - run: + name: Lint.jl version + command: julia -E 'Pkg.installed("Lint")' + - run: + name: Create VFB for Atom to run in + command: /usr/local/bin/xvfb_start + - run: + name: Atom version + command: ${ATOM_SCRIPT_PATH} --version + - run: + name: APM version + command: ${APM_SCRIPT_PATH} --version + - run: + name: Package APM package dependencies + command: | + if [ -n "${APM_TEST_PACKAGES}" ]; then + for pack in ${APM_TEST_PACKAGES}; do + ${APM_SCRIPT_PATH} install "${pack}" + done + fi; + - run: + name: Package dependencies + command: ${APM_SCRIPT_PATH} install + - run: + name: Cleaning package + command: ${APM_SCRIPT_PATH} clean + - run: + name: Package specs + command: ${ATOM_SCRIPT_PATH} --test spec + # Cache node_modules + - save_cache: + paths: + - node_modules + key: v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }}-{{ checksum "package-lock.json"}} + +jobs: + checkout_code: + <<: *defaults + docker: + - image: circleci/node:latest + steps: + - checkout + # Restore node_modules from the last build + - restore_cache: + keys: + # Get latest cache for this package.json and package-lock.json + - v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }}-{{ checksum "package-lock.json"}} + # Fallback to the current package.json + - v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }}- + # Fallback to the last build for this branch + - v1-dependencies-{{ .Branch }}- + # Fallback to the last available master branch cache + - v1-dependencies-master- + # Don't go further down to prevent dependency issues from other branches + # Save project state for next steps + - persist_to_workspace: + root: /tmp + paths: + - project + lint: + <<: *defaults + docker: + - image: circleci/node:lts + steps: + # Restore project state + - attach_workspace: + at: /tmp + - run: + name: Node.js Version + command: node --version + - run: + name: NPM Version + command: npm --version + - run: + name: Install any remaining dependencies + command: npm install + - run: + name: Lint code + command: npm run lint + stable: + <<: *defaults + beta: + <<: *defaults + docker: + - image: arcanemagus/atom-docker-ci:beta + +workflows: + version: 2 + test_package: + jobs: + - checkout_code + - lint: + requires: + - checkout_code + - stable: + requires: + - lint + - beta: + requires: + - lint diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 7e2ebf6..0000000 --- a/circle.yml +++ /dev/null @@ -1,19 +0,0 @@ -dependencies: - override: - - curl -L https://atom.io/download/deb -o atom-amd64.deb - - sudo dpkg --install atom-amd64.deb || true - - sudo apt-get update - - sudo apt-get -f install - - node --version - - npm --version - - atom --version - - npm prune - - npm install - -test: - override: - - npm test - -machine: - node: - version: 6.3.0 diff --git a/lib/index.js b/lib/index.js index 1b53588..8304a5d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,6 +8,7 @@ import type { Server } from './types'; let spawnedServer: ?Server = null; let subscriptions: ?Object = null; +let executablePath; let ignoreInfo; let ignoreWarning; let showErrorCodes; @@ -17,33 +18,36 @@ export function activate() { // eslint-disable-next-line global-require require('atom-package-deps').install('linter-julia'); subscriptions = new CompositeDisposable(); - subscriptions.add(atom.config.observe('linter-julia.executablePath', async (executablePath) => { - if (spawnedServer) { - try { - await terminateServer(spawnedServer); - spawnedServer = null; - spawnedServer = await spawnServer(executablePath); - } catch (e) { - const message = '[Linter-Julia] ' - + 'Unable to spawn server after config change'; - atom.notifications.addError(`${message}. See console for details.`); - // eslint-disable-next-line no-console - console.error(`${message}: `, e); + subscriptions.add( + atom.config.observe('linter-julia.executablePath', async (value) => { + executablePath = value; + if (spawnedServer) { + try { + await terminateServer(spawnedServer); + spawnedServer = null; + spawnedServer = await spawnServer(executablePath); + } catch (e) { + const message = '[Linter-Julia] ' + + 'Unable to spawn server after config change'; + atom.notifications.addError(`${message}. See console for details.`); + // eslint-disable-next-line no-console + console.error(`${message}: `, e); + } } - } - })); - subscriptions.add(atom.config.observe('linter-julia.ignoreInfo', (_ignoreInfo) => { - ignoreInfo = _ignoreInfo; - })); - subscriptions.add(atom.config.observe('linter-julia.ignoreWarning', (_ignoreWarning) => { - ignoreWarning = _ignoreWarning; - })); - subscriptions.add(atom.config.observe('linter-julia.showErrorCodes', (_showErrorCodes) => { - showErrorCodes = _showErrorCodes; - })); - subscriptions.add(atom.config.observe('linter-julia.ignoreIssueCodes', (_ignoreIssueCodes) => { - ignoreIssueCodes = _ignoreIssueCodes; - })); + }), + atom.config.observe('linter-julia.ignoreInfo', (value) => { + ignoreInfo = value; + }), + atom.config.observe('linter-julia.ignoreWarning', (value) => { + ignoreWarning = value; + }), + atom.config.observe('linter-julia.showErrorCodes', (value) => { + showErrorCodes = value; + }), + atom.config.observe('linter-julia.ignoreIssueCodes', (value) => { + ignoreIssueCodes = value; + }), + ); } export function deactivate() { @@ -60,14 +64,14 @@ export function provideLinter() { return { name: 'Julia', scope: 'file', - lintsOnChange: false, + lintsOnChange: true, grammarScopes: ['source.julia'], async lint(textEditor: Object) { if (!spawnedServer) { - spawnedServer = await spawnServer(atom.config.get('linter-julia.executablePath')); + spawnedServer = await spawnServer(executablePath); } const connection = net.createConnection(spawnedServer.path); - connection.on('connect', () => { + connection.on('connect', function writeData() { this.write(JSON.stringify({ file: textEditor.getPath(), code_str: textEditor.getText(), diff --git a/lib/julia-server.jl b/lib/julia-server.jl index ce85665..18fd041 100644 --- a/lib/julia-server.jl +++ b/lib/julia-server.jl @@ -1,9 +1,8 @@ using Lint named_pipe = ARGS[1] -notinstalled = Pkg.installed("Lint") == nothing -if notinstalled +if Pkg.installed("Lint") == nothing print(STDERR, "linter-julia-installing-lint") try Pkg.add("Lint") diff --git a/lib/server.js b/lib/server.js index b05fcd8..93e4226 100644 --- a/lib/server.js +++ b/lib/server.js @@ -12,7 +12,7 @@ const JULIA_SERVER_PATH = Path.join(__dirname, 'julia-server.jl'); export async function getPipePath(): Promise { const baseDir = process.platform === 'win32' ? '\\\\.\\pipe\\' : `${os.tmpdir()}/`; - const uniqueId = uuid.sync(); + const uniqueId = uuid(); return baseDir + uniqueId; } @@ -42,12 +42,13 @@ export async function spawnServer(juliaExecutable: string): Promise { data.stderr += chunk.toString('utf8'); }, exit(exitCode) { - // eslint-disable-next-line no-console - console.debug( - '[Linter-Julia] Server exited with code:', exitCode, - 'STDOUT:', data.stdout, - 'STDERR:', data.stderr, - ); + if (atom.inDevMode()) { + /* eslint-disable no-console */ + console.debug(`[Linter-Julia] Server exited with code: ${exitCode}`); + console.debug(`STDOUT: ${data.stdout}`); + console.debug(`STDERR: ${data.stderr}`); + /* eslint-enable no-console */ + } }, }); diff --git a/package-lock.json b/package-lock.json index a70f53c..d92da22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -927,6 +927,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "jasmine-fix": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jasmine-fix/-/jasmine-fix-1.3.1.tgz", + "integrity": "sha512-jxfPMW5neQUrgEZR7FIXp1UAberYAHkpWTmdSfN/ulU+sC/yUsB827tRiwGUaUyw+1kNC5jqcINst0FF8tvVvg==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index 510f66d..20c20bc 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "eslint": "5.14.0", "eslint-config-airbnb-base": "13.1.0", "eslint-plugin-import": "2.16.0", - "flow-bin": "0.93.0" + "flow-bin": "0.93.0", + "jasmine-fix": "1.3.1" }, "configSchema": { "executablePath": { diff --git a/spec/fixtures/bad.jl b/spec/fixtures/bad.jl new file mode 100644 index 0000000..097092b --- /dev/null +++ b/spec/fixtures/bad.jl @@ -0,0 +1,3 @@ +function theQuestion() + return question +end diff --git a/spec/fixtures/good.jl b/spec/fixtures/good.jl new file mode 100644 index 0000000..2759bf4 --- /dev/null +++ b/spec/fixtures/good.jl @@ -0,0 +1,3 @@ +function theAnswer() + return 42 +end diff --git a/spec/linter-julia-spec.coffee b/spec/linter-julia-spec.coffee deleted file mode 100644 index 4005db7..0000000 --- a/spec/linter-julia-spec.coffee +++ /dev/null @@ -1,62 +0,0 @@ -LinterJulia = require '../lib/linter-julia' - -# Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. -# -# To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` -# or `fdescribe`). Remove the `f` to unfocus the block. - -describe "LinterJulia", -> - [workspaceElement, activationPromise] = [] - - beforeEach -> - workspaceElement = atom.views.getView(atom.workspace) - activationPromise = atom.packages.activatePackage('linter-julia') - - describe "when the linter-julia:toggle event is triggered", -> - it "hides and shows the modal panel", -> - # Before the activation event the view is not on the DOM, and no panel - # has been created - expect(workspaceElement.querySelector('.linter-julia')).not.toExist() - - # This is an activation event, triggering it will cause the package to be - # activated. - atom.commands.dispatch workspaceElement, 'linter-julia:toggle' - - waitsForPromise -> - activationPromise - - runs -> - expect(workspaceElement.querySelector('.linter-julia')).toExist() - - linterJuliaElement = workspaceElement.querySelector('.linter-julia') - expect(linterJuliaElement).toExist() - - linterJuliaPanel = atom.workspace.panelForItem(linterJuliaElement) - expect(linterJuliaPanel.isVisible()).toBe true - atom.commands.dispatch workspaceElement, 'linter-julia:toggle' - expect(linterJuliaPanel.isVisible()).toBe false - - it "hides and shows the view", -> - # This test shows you an integration test testing at the view level. - - # Attaching the workspaceElement to the DOM is required to allow the - # `toBeVisible()` matchers to work. Anything testing visibility or focus - # requires that the workspaceElement is on the DOM. Tests that attach the - # workspaceElement to the DOM are generally slower than those off DOM. - jasmine.attachToDOM(workspaceElement) - - expect(workspaceElement.querySelector('.linter-julia')).not.toExist() - - # This is an activation event, triggering it causes the package to be - # activated. - atom.commands.dispatch workspaceElement, 'linter-julia:toggle' - - waitsForPromise -> - activationPromise - - runs -> - # Now we can test for view visibility - linterJuliaElement = workspaceElement.querySelector('.linter-julia') - expect(linterJuliaElement).toBeVisible() - atom.commands.dispatch workspaceElement, 'linter-julia:toggle' - expect(linterJuliaElement).not.toBeVisible() diff --git a/spec/linter-julia-spec.js b/spec/linter-julia-spec.js new file mode 100644 index 0000000..9663f23 --- /dev/null +++ b/spec/linter-julia-spec.js @@ -0,0 +1,41 @@ +'use babel'; + +import * as path from 'path'; +import { + // eslint-disable-next-line no-unused-vars + it, fit, wait, beforeEach, afterEach, +} from 'jasmine-fix'; + +const { lint } = require('../lib/index.js').provideLinter(); + +const badFile = path.join(__dirname, 'fixtures', 'bad.jl'); +const goodFile = path.join(__dirname, 'fixtures', 'good.jl'); + +// Julia is _slow_ to bring in Lint.jl, increase the timeout to 90 seconds +jasmine.getEnv().defaultTimeoutInterval = 90 * 1000; + +describe('The Julia Lint.jl provider for Linter', () => { + beforeEach(async () => { + atom.workspace.destroyActivePaneItem(); + await atom.packages.activatePackage('linter-julia'); + }); + + it('checks a file with syntax error and reports the correct message', async () => { + const excerpt = 'question: use of undeclared symbol'; + const editor = await atom.workspace.open(badFile); + const messages = await lint(editor); + + expect(messages.length).toBe(1); + expect(messages[0].severity).toBe('error'); + expect(messages[0].excerpt).toBe(excerpt); + expect(messages[0].location.file).toBe(badFile); + // NOTE: This is invalid! Bug in Lint.jl + expect(messages[0].location.position).toEqual([[1, 0], [1, 80]]); + }); + + it('finds nothing wrong with a valid file', async () => { + const editor = await atom.workspace.open(goodFile); + const messages = await lint(editor); + expect(messages.length).toBe(0); + }); +});