Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"node": "*"
},
"dependencies": {
"glob": "^7.1.2",
"glob": "^7.1.3",
"request": "^2.83.0",
"request-promise-native": "^1.0.5",
"tree-sitter": "^0.13.22",
Expand All @@ -30,5 +30,8 @@
"compile": "rm -rf out && ../node_modules/.bin/tsc -p ./",
"compile:watch": "../node_modules/.bin/tsc -w -p ./",
"prepublishOnly": "yarn run compile"
},
"devDependencies": {
"@types/glob": "^7.1.1"
}
}
73 changes: 44 additions & 29 deletions server/src/analyser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// tslint:disable:no-submodule-imports
import * as fs from 'fs'
import * as glob from 'glob'
import * as Path from 'path'

import * as request from 'request-promise-native'
import * as Parser from 'tree-sitter'
Expand All @@ -12,6 +11,8 @@ import * as LSP from 'vscode-languageserver'
import { uniqueBasedOnHash } from './util/array'
import { flattenArray, flattenObjectValues } from './util/flatten'
import * as TreeSitterUtil from './util/tree-sitter'
import { hasBashShebang } from './util/shebang';
import { getGlobPattern } from './config';

type Kinds = { [type: string]: LSP.SymbolKind }

Expand All @@ -33,41 +34,55 @@ export default class Analyzer {
* If the rootPath is provided it will initialize all *.sh files it can find
* anywhere on that path.
*/
public static fromRoot(
public static async fromRoot(
connection: LSP.Connection,
rootPath: string | null,
): Promise<Analyzer> {
// This happens if the users opens a single bash script without having the
// 'window' associated with a specific project.
if (!rootPath) {
return Promise.resolve(new Analyzer())
const analyzer = new Analyzer()

if (rootPath) {
const lookupStartTime = Date.now()

const globPattern = getGlobPattern()
connection.console.log(`Looking up files matching "${globPattern}"`)

const filePaths = await this.getFilePaths({globPattern, rootPath})

filePaths.forEach(filePath => {
const fileContent = fs.readFileSync(filePath, 'utf8')
if (!hasBashShebang(fileContent)) {
connection.console.log(`No bash shebang found for ${filePath}`)
return
}

connection.console.log(`Analyzing ${filePath}`)

const uri = 'file://' + filePath
analyzer.analyze(
uri,
LSP.TextDocument.create(
uri,
'shell',
1,
fileContent,
),
)
})

connection.console.log(`Analyzing finished after ${(Date.now() - lookupStartTime)/1000} seconds`)
}

return analyzer
}

private static getFilePaths({globPattern, rootPath}:{ globPattern: string, rootPath: string}): Promise<string[]> {
return new Promise((resolve, reject) => {
glob('**/*.sh', { cwd: rootPath }, (err, paths) => {
if (err != null) {
reject(err)
} else {
const analyzer = new Analyzer()
paths.forEach(p => {
const absolute = Path.join(rootPath, p)
// only analyze files, glob pattern may match directories
if (fs.existsSync(absolute) && fs.lstatSync(absolute).isFile()) {
const uri = 'file://' + absolute
connection.console.log('Analyzing ' + uri)
analyzer.analyze(
uri,
LSP.TextDocument.create(
uri,
'shell',
1,
fs.readFileSync(absolute, 'utf8'),
),
)
}
})
resolve(analyzer)
glob(globPattern, { cwd: rootPath, nodir: true, absolute: true }, function (err, files) {
if (err) {
return reject(err)
}

resolve(files)
})
})
}
Expand Down
5 changes: 5 additions & 0 deletions server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ export function getExplainshellEndpoint(): string | null {
return typeof EXPLAINSHELL_ENDPOINT !== 'undefined' ? EXPLAINSHELL_ENDPOINT : null
}

export function getGlobPattern(): string {
const { GLOB_PATTERN } = process.env
return typeof GLOB_PATTERN === 'string' ? GLOB_PATTERN : '**/*@(.sh|.inc|.bash|.command)'
}

export function getHighlightParsingError(): boolean {
const { HIGHLIGHT_PARSING_ERRORS } = process.env
return typeof HIGHLIGHT_PARSING_ERRORS !== 'undefined'
Expand Down
31 changes: 31 additions & 0 deletions server/src/util/__tests__/shebang.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {hasBashShebang} from '../shebang'

describe('hasBashShebang', () => {
it('returns false for empty file', () => {
expect(hasBashShebang('')).toBe(false)
})

it('returns false for python files', () => {
expect(hasBashShebang(`#!/usr/bin/env python2.7\n# set -x`)).toBe(false)
})

it('returns true for "#!/bin/sh -"', () => {
expect(hasBashShebang('#!/bin/sh -')).toBe(true)
expect(hasBashShebang('#!/bin/sh - ')).toBe(true)
})

it('returns true for "#!/usr/bin/env bash"', () => {
expect(hasBashShebang('#!/usr/bin/env bash')).toBe(true)
expect(hasBashShebang('#!/usr/bin/env bash ')).toBe(true)
})

it('returns true for "#!/bin/sh"', () => {
expect(hasBashShebang('#!/bin/sh')).toBe(true)
expect(hasBashShebang('#!/bin/sh ')).toBe(true)
})

it('returns true for "#!/bin/bash"', () => {
expect(hasBashShebang('#!/bin/bash')).toBe(true)
expect(hasBashShebang('#!/bin/bash ')).toBe(true)
})
})
11 changes: 11 additions & 0 deletions server/src/util/shebang.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const SHEBANG_REGEXP = /^#!(.*)/

export function hasBashShebang(fileContent: string) {
const match = SHEBANG_REGEXP.exec(fileContent)
if (!match || !match[1]) {
return false
}

const shebang = match[1].replace('-', '').trim()
return shebang.endsWith('bash') || shebang.endsWith('sh')
}
32 changes: 28 additions & 4 deletions server/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@
# yarn lockfile v1


"@types/events@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==

"@types/glob@^7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
dependencies:
"@types/events" "*"
"@types/minimatch" "*"
"@types/node" "*"

"@types/minimatch@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==

"@types/node@*":
version "11.11.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a"
integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==

abab@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"
Expand Down Expand Up @@ -373,10 +397,10 @@ github-from-package@0.0.0:
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=

glob@^7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
glob@^7.1.3:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
Expand Down
4 changes: 4 additions & 0 deletions testing/fixtures/include.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh

export XXX='my export'

5 changes: 5 additions & 0 deletions testing/fixtures/included.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
. $(dirname "$0")/include.inc

echo ${XXX}

8 changes: 8 additions & 0 deletions testing/fixtures/no-extension
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
configures="`env | grep 'npm_config_' | sed -e 's|^npm_config_||g'`"

ret=$?
if [ $ret -ne 0 ]; the
echo "It failed" >&2
fi
exit $ret
5 changes: 5 additions & 0 deletions testing/fixtures/not-shell.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python2.7
# set -x

def func():
print 'hello world'