diff --git a/ui/frontend/.eslintrc.js b/ui/frontend/.eslintrc.js index 92d5fe370..de16ebb19 100644 --- a/ui/frontend/.eslintrc.js +++ b/ui/frontend/.eslintrc.js @@ -97,6 +97,7 @@ module.exports = { 'reducers/selection.ts', 'reducers/versions.ts', 'reducers/websocket.ts', + 'selectors/index.spec.ts', 'types.ts', 'websocketActions.ts', 'websocketMiddleware.ts', diff --git a/ui/frontend/.prettierignore b/ui/frontend/.prettierignore index 543e3cee9..ed34c38c6 100644 --- a/ui/frontend/.prettierignore +++ b/ui/frontend/.prettierignore @@ -48,6 +48,7 @@ node_modules !reducers/selection.ts !reducers/versions.ts !reducers/websocket.ts +!selectors/index.spec.ts !types.ts !websocketActions.ts !websocketMiddleware.ts diff --git a/ui/frontend/selectors/index.spec.ts b/ui/frontend/selectors/index.spec.ts index bc443e3b4..98d1f51bf 100644 --- a/ui/frontend/selectors/index.spec.ts +++ b/ui/frontend/selectors/index.spec.ts @@ -1,60 +1,66 @@ import reducer from '../reducers'; import { editCode } from '../reducers/code'; - import { hasMainFunctionSelector } from './index'; -const buildState = (code: string) => { - const state = reducer(undefined, editCode(code)); - return state; -}; +const buildState = (code: string) => reducer(undefined, editCode(code)); + +const doMainFunctionSelector = (code: string) => hasMainFunctionSelector(buildState(code)); describe('checking for a main function', () => { test('empty code has no main', () => { - expect(hasMainFunctionSelector(buildState(''))).toBe(false); + expect(doMainFunctionSelector('')).toBe(false); }); test('a plain main counts', () => { - expect(hasMainFunctionSelector(buildState('fn main()'))).toBe(true); + expect(doMainFunctionSelector('fn main()')).toBe(true); }); test('a public main counts', () => { - expect(hasMainFunctionSelector(buildState('pub fn main()'))).toBe(true); + expect(doMainFunctionSelector('pub fn main()')).toBe(true); }); test('an async main counts', () => { - expect(hasMainFunctionSelector(buildState('async fn main()'))).toBe(true); + expect(doMainFunctionSelector('async fn main()')).toBe(true); }); test('a public async main counts', () => { - expect(hasMainFunctionSelector(buildState('pub async fn main()'))).toBe(true); + expect(doMainFunctionSelector('pub async fn main()')).toBe(true); }); test('a const main counts', () => { - expect(hasMainFunctionSelector(buildState('const fn main()'))).toBe(true); + expect(doMainFunctionSelector('const fn main()')).toBe(true); }); test('a public const main counts', () => { - expect(hasMainFunctionSelector(buildState('pub const fn main()'))).toBe(true); + expect(doMainFunctionSelector('pub const fn main()')).toBe(true); }); test('a public const async main counts', () => { - expect(hasMainFunctionSelector(buildState('pub const async fn main()'))).toBe(true); + expect(doMainFunctionSelector('pub const async fn main()')).toBe(true); }); test('leading indentation is ignored', () => { - expect(hasMainFunctionSelector(buildState('\t fn main()'))).toBe(true); + expect(doMainFunctionSelector('\t fn main()')).toBe(true); }); test('extra space everywhere is ignored', () => { - expect(hasMainFunctionSelector(buildState(' pub async fn main ( )'))).toBe(true); + expect(doMainFunctionSelector(' pub async fn main ( )')).toBe(true); }); test('a commented-out main does not count', () => { - expect(hasMainFunctionSelector(buildState('// fn main()'))).toBe(false); - expect(hasMainFunctionSelector(buildState('/* fn main()'))).toBe(false); + expect(doMainFunctionSelector('// fn main()')).toBe(false); + expect(doMainFunctionSelector('/* fn main()')).toBe(false); }); test('a function with the substring main does not count', () => { - expect(hasMainFunctionSelector(buildState('fn mainly()'))).toBe(false); + expect(doMainFunctionSelector('fn mainly()')).toBe(false); + }); + + test('a main function after other items on the same line', () => { + expect(doMainFunctionSelector('use std; fn main(){ println!("Hello, world!"); }')).toBe(true); + }); + + test('a main function with a block comment in the argument list', () => { + expect(doMainFunctionSelector('fn main(/* comment */) {')).toBe(true); }); }); diff --git a/ui/frontend/selectors/index.ts b/ui/frontend/selectors/index.ts index 462e1676a..0fcfa92f6 100644 --- a/ui/frontend/selectors/index.ts +++ b/ui/frontend/selectors/index.ts @@ -20,7 +20,15 @@ export const selectionSelector = (state: State) => state.selection; const HAS_TESTS_RE = /^\s*#\s*\[\s*test\s*([^"]*)]/m; export const hasTestsSelector = createSelector(codeSelector, code => !!code.match(HAS_TESTS_RE)); -const HAS_MAIN_FUNCTION_RE = /^\s*(pub\s+)?\s*(const\s+)?\s*(async\s+)?\s*fn\s+main\s*\(\s*\)/m; +// https://stackoverflow.com/a/34755045/155423 +const HAS_MAIN_FUNCTION_RE = new RegExp( + [ + /^([^\n\r\/]*;)?/, + /\s*(pub\s+)?\s*(const\s+)?\s*(async\s+)?\s*/, + /fn\s+main\s*\(\s*(\/\*.*\*\/)?\s*\)/, + ].map((r) => r.source).join(''), + 'm' +); export const hasMainFunctionSelector = createSelector(codeSelector, code => !!code.match(HAS_MAIN_FUNCTION_RE)); const CRATE_TYPE_RE = /^\s*#!\s*\[\s*crate_type\s*=\s*"([^"]*)"\s*]/m;