Skip to content

Commit d0bb2c7

Browse files
committed
Merge pull request #7 from montegoulding/feature/linter
Feature/linter
2 parents b9f97a0 + 6857cd6 commit d0bb2c7

File tree

8 files changed

+31330
-1
lines changed

8 files changed

+31330
-1
lines changed

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,19 @@ Currently, this package supports editing:
1919

2020
* LiveCode Builder source code in `.lcb` files
2121
* LiveCode server source code in `.lc` and `.irev` files
22+
* LiveCode Script source code in `.livecodescript` files, where the shebang line
23+
contains `livecode` or the first line contains an Emacs mode comment
24+
`# -*- mode:livecodescript -*-`
2225

2326
The package provides syntax highlighting, highlighting and indentation support
2427
for all the supported languages.
2528

2629
It also provides snippets to enhance LiveCode server script and revIgniter
2730
script development.
2831

32+
For LiveCode Script there are quite a number of autocomplete snippets generated
33+
from the LiveCode documentation.
34+
2935
## Installation
3036

3137
Install the `language-livecode` package from the "Install Packages and Themes"
@@ -34,11 +40,35 @@ view in Atom's "Settings" window.
3440
You may also wish to install the [revIgniter theme](https://atom.io/themes/revigniter-syntax)
3541
for Atom.
3642

43+
## Script Error Checking
44+
45+
This package provides a linter for LiveCode Script and Server to highlight and describe
46+
script compilation errors on the fly.
47+
48+
![Linter In Action](http://ecove.on-rev.com/linter.gif)
49+
50+
Dependencies:
51+
52+
* The [linter package](https://atom.io/packages/linter) needs to be installed
53+
* LiveCode 7.1+ Server engine for the platform it is being run on must be accessible
54+
55+
The default setting assumes you have installed the LiveCode Server engine somewhere
56+
on the current `$PATH` with the name `livecode-server`. However, you can enter any
57+
path or name you choose via the package settings.
58+
59+
For LiveCode Server the linter will only check for errors within the scope of
60+
the file being edited. If for example a variable re-declarition error is caused
61+
by the inclusion of another file that error won't be detected.
62+
63+
The linter supports an optional explicit variables mode which can be turned on
64+
via the package settings.
65+
3766
## Authors
3867

3968
* Ralf Bitter
4069
* Peter Brett
4170
* Adam Robertson
71+
* Monte Goulding
4272

4373
## Reporting bugs and contributing
4474

grammars/livecodescript.cson

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# If this is your first time writing a language grammar, check out:
2+
# - http://manual.macromates.com/en/language_grammars
3+
4+
'scopeName': 'source.livecodescript'
5+
'name': 'LiveCode Script'
6+
'fileTypes': [
7+
'livecodescript'
8+
]
9+
'firstLineMatch': '^#!.*\\b(livecode)|^#\\s*-\\*-[^*]*mode:\\s*livecodescript[^*]*-\\*-'
10+
'patterns': [
11+
{
12+
include: "#language"
13+
}
14+
]
15+
repository:
16+
constants:
17+
patterns: [
18+
{
19+
match: "(?i)\\b(SIX|TEN|FORMFEED|NINE|ZERO|NONE|SPACE|FOUR|FALSE|COLON|CRLF|PI|COMMA|ENDOFFILE|EOF|EIGHT|FIVE|QUOTE|EMPTY|ONE|TRUE|((?<=\\w\\s)RETURN)|CR|LINEFEED|RIGHT|BACKSLASH|NULL|SEVEN|TAB|THREE|TWO)\\b"
20+
name: "constant.language.livecodescript"
21+
}
22+
]
23+
language:
24+
patterns: [
25+
{
26+
begin: "/\\*"
27+
captures:
28+
"0":
29+
name: "punctuation.definition.comment.livecodescript"
30+
end: "\\*/"
31+
name: "comment.block.livecodescript"
32+
}
33+
{
34+
captures:
35+
"1":
36+
name: "punctuation.definition.comment.livecodescript"
37+
match: "(--).*?($\\n?)"
38+
name: "comment.line.double-dash.livecodescript"
39+
}
40+
{
41+
captures:
42+
"1":
43+
name: "punctuation.definition.comment.livecodescript"
44+
match: "(#).*?($\\n?)"
45+
name: "comment.line.number-sign.livecodescript"
46+
}
47+
{
48+
captures:
49+
"1":
50+
name: "punctuation.definition.comment.livecodescript"
51+
match: "(//).*?($\\n?)"
52+
name: "comment.line.number-sign.livecodescript"
53+
}
54+
{
55+
match: "\\b(after|byte(s)*|codepoint(s)*|codeunit(s)*|english|segment(s)*|sentence(s)*|paragraph|the|trueWord(s)*|until|word(s)*|http|forever|descending|using|line|real8|with|seventh|for|stdout|finally|element|word|fourth|before|black|ninth|sixth|characters|chars|stderr|uInt(1|1s|2|2s)|stdin|string|lines|relative|rel|any|fifth|items|from|middle|mid|at|else|of|catch|then|third|it|file|milli(seconds|second|secs|sec)|int(1|1s|4|4s|ernet|2|2s)|normal|text|item|last|long|detailed|effective|uInt4|uInt4s|se(conds|cond|cs|c)|repeat|end\\s+repeat|URL|in|end\\s+try|into|switch|end\\s+switch|to|words|https|token|binfile|each|tenth|as|ticks|tick|system|real4|by|dateItems|without|cha(r|racter)|ascending|eighth|whole|dateTime|numeric|short|first|ftp|integer|abbreviated|abb(r|rev)|private|case|while|if|end\\s+if)\\b"
56+
name: "keyword.control.livecodescript"
57+
}
58+
{
59+
captures:
60+
"1":
61+
name: "keyword.control.exception.livecodescript"
62+
match: "\\b(catch)\\b\\s*([A-Za-z_][A-Za-z_0-9]*)"
63+
name: "meta.catch.livecodescript"
64+
}
65+
{
66+
match: "\\b(catch|try|throw)\\b"
67+
name: "keyword.control.exception.livecodescript"
68+
}
69+
{
70+
captures:
71+
"1":
72+
name: "storage.type.function.livecodescript"
73+
"2":
74+
name: "entity.name.function.livecodescript"
75+
"3":
76+
name: "variable.parameter.livecodescript"
77+
match: "((?i)function)\\s+([A-Za-z_0-9-]+)(\\s+[A-Za-z_0-9-]+,*?\\s+.*)*"
78+
name: "meta.function.livecodescript"
79+
}
80+
{
81+
captures:
82+
"1":
83+
name: "storage.type.function.end.livecodescript"
84+
"2":
85+
name: "entity.name.end.function.livecodescript"
86+
match: "((?i)end){1}\\s+?([A-Za-z_0-9-]+)?"
87+
name: "meta.function.end.livecodescript"
88+
}
89+
{
90+
captures:
91+
"1":
92+
name: "storage.type.command.livecodescript"
93+
"2":
94+
name: "entity.name.command.livecodescript"
95+
"3":
96+
name: "variable.parameter.livecodescript"
97+
match: "((?i)before|after|command|on)\\s+([A-Za-z_0-9-]+)(\\s+[A-Za-z_0-9-]+,*?\\s+.*)*"
98+
name: "meta.command.livecodescript"
99+
}
100+
{
101+
captures:
102+
"1":
103+
name: "storage.type.test.livecodescript"
104+
"2":
105+
name: "entity.name.test.livecodescript"
106+
match: "((?i)blah){1}\\s+?([A-Za-z_0-9-]+)?"
107+
name: "meta.test.livecodescript"
108+
}
109+
{
110+
match: "((command|on)\\s+([A-Za-z_0-9-]+))"
111+
name: "meta.testA.livecodescript"
112+
}
113+
{
114+
match: "(\\-|\\+|\\*|/|%)"
115+
name: "keyword.operator.arithmetic.livecodescript"
116+
}
117+
{
118+
match: "(?i)\\b(and|or)\\b"
119+
name: "keyword.operator.logical.livecodescript"
120+
}
121+
{
122+
match: "(?i)\\b(bitAnd|bitNot|bitOr|bitXor)\\b"
123+
name: "keyword.operator.bitwise.livecodescript"
124+
}
125+
{
126+
match: "(?i)(=|<>|>=|<=|<|>)|\\b(is( among| not among| not in| a| an| not| in| not within| within| not a| not an)*|there is( no| not a| not an| a| an)|contains|ends with|begins with|is among the keys of|is not among the keys of)\\b"
127+
name: "keyword.operator.comparison.livecodescript"
128+
}
129+
{
130+
match: "(&|&&)"
131+
name: "keyword.operator.string.livecodescript"
132+
}
133+
{
134+
match: "(\\^)|\\b(div|mod|wrap)\\b"
135+
name: "keyword.operator.other.livecodescript"
136+
}
137+
{
138+
match: "(\\^)|\\b(abs|acos|aliasReference|annuity|arrayDecode|arrayEncode|asin|atan(2)*|average(Deviation)*|avg(Dev)*|base64Decode|base64Encode|baseConvert|binaryDecode|binaryEncode|byte(Offset|ToNum)+|cachedURL(s)*|charToNum|cipherNames|codepoint(Offset|Property|ToNum)+|codeunitOffset|commandNames|compound|compress|constantNames|cos|date(Format)*|decompress|directories|diskSpace|DNSServers|exp(1|2|10)*|extents|files|flushEvents|folders|format|functionNames|geometricMean|global(s|Names)+|harmonicMean|hasMemory|hostAddress|hostAddressToName|hostName(ToAddress)*|isNumber|ISOToMac|itemOffset|keys|len(gth)*|libURLErrorData|libUrlFormData|libURLftpCommand|libURLLastHTTPHeaders|libURLLastRHHeaders|libUrlMultipartFormAddPart|libUrlMultipartFormData|libURLVersion|lineOffset|ln(1)*|localNames|log(2|10)*|longFilePath|lower|macToISO|matchChunk|matchText|matrixMultiply|max|md5Digest|median|merge|milli(sec|secs|second|seconds)+|min|monthNames|nativeCharToNum|normalizeText|num(ber|ToByte|ToChar|ToCodepoint|ToNativeChar)*|offset|open(files|Processes)*|openProcessIDs|openSockets|paragraphOffset|paramCount|param(s)*|peerAddress|pendingMessages|platform|popStdDev|populationStandardDeviation|popVariance|populationVariance|processID|random(Bytes)*|replaceText|result|revCreateXMLTree|revCreateXMLTreeFromFile|revCurrentRecord|revCurrentRecordIsFirst|revCurrentRecordIsLast|revDatabaseColumnCount|revDatabaseColumnIsNull|revDatabaseColumnLengths|revDatabaseColumnName(s|d)+|revDatabaseColumnNumbered|revDatabaseColumnTypes|revDatabaseConnectResult|revDatabaseCursors|revDatabaseID|revDatabaseTableNames|revDatabaseType|revDataFromQuery|revdb_closeCursor|revdb_columnbynumber|revdb_columncount|revdb_columnisnull|revdb_columnlengths|revdb_columnnames|revdb_columntypes|revdb_commit|revdb_connect(ions)*|revdb_connectionerr|revdb_currentrecord|revdb_cursorconnection|revdb_cursorerr|revdb_cursors|revdb_dbtype|revdb_disconnect|revdb_execute|revdb_is(eof|bof)+|revdb_movefirst|revdb_movelast|revdb_movenext|revdb_moveprev|revdb_query|revdb_querylist|revdb_recordcount|revdb_rollback|revdb_tablenames|revGetDatabaseDriverPath|revNumberOfRecords|revOpenDatabase(s)*|revQueryDatabase(Blob)*|revQuery(Result|IsAtStart|IsAtEnd)+|revUnixFromMacPath|revXMLAttribute(s)*|revXMLAttributeValues|revXMLChildContents|revXMLChildNames|revXMLCreateTreeFromFileWithNamespaces|revXMLCreateTreeWithNamespaces|revXMLDataFromXPathQuery|revXMLEvaluateXPath|revXMLFirstChild|revXMLMatchingNode|revXMLNextSibling|revXMLNodeContents|revXMLNumberOfChildren|revXMLParent|revXMLPreviousSibling|revXMLRootNode|revXMLRPC_CreateRequest|revXMLRPC_Documents|revXMLRPC_Error|revXMLRPC_Execute|revXMLRPC_GetHost|revXMLRPC_GetMethod|revXMLRPC_GetParam|revXMLText|revXMLRPC_GetParamCount|revXMLRPC_GetParamNode|revXMLRPC_GetParamType|revXMLRPC_GetPath|revXMLRPC_GetPort|revXMLRPC_GetProtocol|revXMLRPC_GetRequest|revXMLRPC_GetResponse|revXMLRPC_GetSocket|revXMLTree(s)*|revXMLValidateDTD|revZipDescribeItem|revZipEnumerateItems|revZipOpenArchives|round|sampVariance|sec(s|onds)*|sentenceOffset|sha1Digest|shell|shortFilePath|sin|specialFolderPath|sqrt|standardDeviation|statRound|stdDev|sum|sysError|systemVersion|tan|tempName|textDecode|textEncode|tick(s)*|time|to(Lower|Upper)*|tokenOffset|transpose|truewordOffset|trunc|uniDecode|uniEncode|upper|urlDecode|urlEncode|urlStatus|uuid|value|variableNames|variance|version|waitDepth|weekdayNames|wordOffset|xsltApplyStylesheet|xsltApplyStylesheetFromFile|xsltLoadStylesheet|xsltLoadStylesheetFromFile)\\b"
139+
name: "storage.type.fctn.livecodescript"
140+
}
141+
{
142+
match: "(\\^)|\\b(add|breakpoint|cancel|clear( local| global| variable| file| word| line| folder| directory| URL)*|close( file| socket| process)*|combine|constant|convert|(create|new)( alias| folder| directory)*|decrypt( using rsa)*|delete( directory| file| folder| global| line| local| session| URL| variable| word)*|dispatch|divide|do|encrypt( using rsa)*|filter|get|global|include|intersect|kill|libURLDownloadToFile|libURLFollowHttpRedirects|libURLftpUpload(File)*|libURLresetAll|libUrlSetAuthCallback|libURLSetCustomHTTPHeaders|libUrlSetExpect100|libURLSetFTPListCommand|libURLSetFTPMode|libURLSetFTPStopTime|libURLSetStatusCallback|load URL|local|multiply|open( socket| file| process)*|post|prepare|put( binary| content| cookie| header| markup| unicode)*|read( from process| from socket| from file)*|rename|replace|require|resetAll|resolve|revAddXMLNode|revAppendXML|revCloseCursor|revCloseDatabase|revCommitDatabase|revCopyFile|revCopyFolder|revCopyXMLNode|revDeleteFolder|revDeleteXMLNode|revDeleteAllXMLTrees|revDeleteXMLTree|revExecuteSQL|revGoURL|revInsertXMLNode|revMoveFolder|revMoveToFirstRecord|revMoveToLastRecord|revMoveToNextRecord|revMoveToPreviousRecord|revMoveToRecord|revMoveXMLNode|revPutIntoXMLNode|revRollBackDatabase|revSetDatabaseDriverPath|revSetXMLAttribute|revXMLRPC_AddParam|revXMLRPC_DeleteAllDocuments|revXMLAddDTD|revXMLRPC_Free|revXMLRPC_FreeAll|revXMLRPC_DeleteDocument|revXMLRPC_DeleteParam|revXMLRPC_SetHost|revXMLRPC_SetMethod|revXMLRPC_SetPort|revXMLRPC_SetProtocol|revXMLRPC_SetSocket|revZipAddItemWithData|revZipAddItemWithFile|revZipAddUncompressedItemWithData|revZipAddUncompressedItemWithFile|revZipCancel|revZipCloseArchive|revZipDeleteItem|revZipExtractItemToFile|revZipExtractItemToVariable|revZipSetProgressCallback|revZipRenameItem|revZipReplaceItemWithData|revZipReplaceItemWithFile|revZipOpenArchive|seek( to| rel| relative)*|send|set|sort|split|start( session)*|stop( session)*|subtract|union|unload( URL)*|wait|write)\\b"
143+
name: "storage.type.comnd.livecodescript"
144+
}
145+
{
146+
include: "#constants"
147+
}
148+
{
149+
include: "#support"
150+
}
151+
{
152+
include: "#numbers"
153+
}
154+
{
155+
include: "#strings"
156+
}
157+
{
158+
include: "#variables"
159+
}
160+
]
161+
numbers:
162+
match: "\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)\\b"
163+
name: "constant.numeric.livecodescript"
164+
"string-quoted":
165+
begin: "\""
166+
beginCaptures:
167+
"0":
168+
name: "punctuation.definition.string.begin.livecodescript"
169+
contentName: "meta.string-contents.quoted.double.livecodescript"
170+
end: "\""
171+
endCaptures:
172+
"0":
173+
name: "punctuation.definition.string.end.livecodescript"
174+
name: "string.quoted.double.livecodescript"
175+
patterns: [
176+
{
177+
include: "#interpolation"
178+
}
179+
]
180+
strings:
181+
patterns: [
182+
{
183+
include: "#string-quoted"
184+
}
185+
]
186+
var_global:
187+
captures:
188+
"1":
189+
name: "punctuation.definition.variable.livecodescript"
190+
match: "\\b(g)[A-Z]{1}.*?\\b"
191+
name: "variable.other.global.livecodescript"
192+
var_local:
193+
captures:
194+
"1":
195+
name: "punctuation.definition.variable.livecodescript"
196+
match: "\\b(t|the)[A-Z]{1}.*?\\b"
197+
name: "variable.other.global.livecodescript"
198+
var_parameter:
199+
captures:
200+
"1":
201+
name: "punctuation.definition.variable.livecodescript"
202+
match: "\\b(p)[A-Z]{1}.*?\\b"
203+
name: "variable.parameter.livecodescript"
204+
var_scriptLocal:
205+
captures:
206+
"1":
207+
name: "punctuation.definition.variable.livecodescript"
208+
match: "\\b(s)[A-Z]{1}.*?\\b"
209+
name: "variable.other.global.livecodescript"
210+
var_server:
211+
match: "\\$_[A-Z]+"
212+
name: "variable.other.server.livecodescript"
213+
variables:
214+
patterns: [
215+
{
216+
include: "#var_global"
217+
}
218+
{
219+
include: "#var_scriptLocal"
220+
}
221+
{
222+
include: "#var_local"
223+
}
224+
{
225+
include: "#var_parameter"
226+
}
227+
{
228+
include: "#var_server"
229+
}
230+
]

lib/main.coffee

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{CompositeDisposable} = require 'atom'
2+
3+
module.exports =
4+
config:
5+
executablePath:
6+
type: 'string'
7+
title: 'LiveCode Server Engine Path'
8+
default: 'livecode-server' # Let OS's $PATH handle the rest
9+
description: 'Where is your livecode server installed? ' +
10+
'Default assumes it\'s on $PATH'
11+
explicitVars:
12+
type: 'boolean'
13+
title: 'Explicit Variables'
14+
default: 'false'
15+
description: 'Get errors on undeclared variables'
16+
17+
activate: ->
18+
@subscriptions = new CompositeDisposable
19+
@subscriptions.add atom.config.observe 'language-livecode.executablePath',
20+
(executablePath) =>
21+
@executablePath = executablePath
22+
@subscriptions.add atom.config.observe 'language-livecode.explicitVars',
23+
(explicitVars) =>
24+
@explicitVars = explicitVars
25+
path = require 'path'
26+
@linterPath = path.join(__dirname, '..', 'tools', 'Linter.lc')
27+
28+
deactivate: ->
29+
@subscriptions.dispose()
30+
31+
provideLinter: ->
32+
helpers = require('atom-linter')
33+
provider =
34+
grammarScopes: ['source.livecodescript', 'source.iRev']
35+
scope: 'file'
36+
lintOnFly: true
37+
lint: (textEditor) =>
38+
filePath = textEditor.getPath()
39+
command = @executablePath
40+
parameters = []
41+
stackfile = @linterPath
42+
parameters.push(stackfile)
43+
scope = '-scope=' + textEditor.getRootScopeDescriptor()
44+
parameters.push(scope)
45+
explicitVariables = '-explicitVariables=' + @explicitVars
46+
parameters.push(explicitVariables)
47+
text = textEditor.getText()
48+
return helpers.exec(command, parameters, {stdin: text}).then (output) ->
49+
regex = /(\d+),(\d+),(.*)/g
50+
messages = []
51+
while((match = regex.exec(output)) isnt null)
52+
line = match[1]-1
53+
messages.push
54+
type: "Error"
55+
filePath: filePath
56+
range: [
57+
[line, match[2]-0],
58+
[line, textEditor.getBuffer().lineLengthForRow(line)]
59+
]
60+
text: match[3]
61+
return messages

package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,16 @@
66
"license": "GPLv3",
77
"engines": {
88
"atom": "*"
9+
},
10+
"main": "./lib/main",
11+
"dependencies": {
12+
"atom-linter": "^3.0.0"
13+
},
14+
"providedServices": {
15+
"linter": {
16+
"versions": {
17+
"1.0.0": "provideLinter"
18+
}
19+
}
920
}
1021
}

0 commit comments

Comments
 (0)