Skip to content

Commit

Permalink
Merge pull request #48 from idris-hackers/ipkg
Browse files Browse the repository at this point in the history
add Ipkg support
  • Loading branch information
archaeron committed Sep 18, 2015
2 parents 6ba5aec + cbd17c7 commit 29bf15d
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Added

- Add a means of setting the Idris -p option [#29](https://github.com/idris-hackers/atom-language-idris/issues/29)

### Fixed

## v0.2.5
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ It supports:
- make-case (ctrl-alt-m)
- make-lemma (ctrl-alt-l)
- Showing holes
- ipkg highlighting

## Usage

Expand All @@ -23,6 +24,11 @@ If it doesn't work it's probably a bug.

There is a tutorial on how to use the editor under [`documentation/tutorial.md`](https://github.com/idris-hackers/atom-language-idris/blob/master/documentation/tutorial.md).

### Working with ipkg files

Place your ipkg file in the top level directory of your project.
There is more information available in a in a [separate documentation](https://github.com/idris-hackers/atom-language-idris/blob/master/documentation/ipkg.md).

## Todo

- Add better support for drawing attention to error-messages
Expand All @@ -36,3 +42,6 @@ There is a tutorial on how to use the editor under [`documentation/tutorial.md`]
To work on this plugin you need to clone it into your atom directory
and rename the folder to `language-idris` or the package settings won't get picked up.
Then you need an `apm install` from the `language-idris` folder to install the dependencies.

Or you can execute `apm dev language-idris`. This will install the package in a separate directory and you need to start
Atom in dev-mode to load the development packages.
37 changes: 37 additions & 0 deletions documentation/ipkg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# IPKG files

When you open a directory with a file at the top level in it that ends in `.ipkg`, all the commands
in this package will read it and use it to find the path of your sources and resolve
dependencies.

Supported are the `opts` and `sourcedir` options.

There is [more information](http://docs.idris-lang.org/en/latest/tutorial/packages.html) about `ipkg`-files in the idris documentation.


## Example

You have a folder that looks like this:

```
src
└───Main.idr
└───OtherFile.idr
your-project.ipkg
```

with `your-project.ipkg` containing:

```
package yourProject
sourcedir = src
modules = Main
executable = yourExecutable
main = Main
opts = "-p lightyear"
```

the package will search in the `src`-directory for your files and will
load the dependencies specified in `opts`.
15 changes: 15 additions & 0 deletions grammars/language-ipkg.cson
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: 'Idris Ipkg'
scopeName: 'source.ipkg'
fileTypes: ['ipkg']
patterns:
[
{
name: 'keyword.control.ipkg'
match: '\\b(package|opts|modules|sourcedir|makefile|objs|executable|main|libs)\\b'
}
{
name: 'string.quoted.double.ipkg'
begin: '"'
end: '"'
}
]
15 changes: 12 additions & 3 deletions lib/idris-controller.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ HolesView = require './views/holes-view'
StatusIndicator = require './views/status-indicator-view'
Logger = require './Logger'
IdrisModel = require './idris-model'
Ipkg = require './utils/ipkg'

class IdrisController

Expand Down Expand Up @@ -38,23 +39,31 @@ class IdrisController
cursorPosition = editor.getLastCursor().getCurrentWordBufferRange options
editor.getTextInBufferRange cursorPosition

initialize: ->
initialize: (compilerOptions) ->
if !@model
@model = new IdrisModel
@messages = new MessagePanelView
title: 'Idris Messages'
closeMethod: 'hide'
@messages.attach()
@messages.hide()
@model.setCompilerOptions compilerOptions

stopCompiler: =>
@model?.stop()

runCommand:
(command) =>
(args) =>
@initialize()
command args
compilerOptions = Ipkg.compilerOptions atom.project
compilerOptions.subscribe ((options) =>
console.log "Compiler Options:", options
@initialize options
command args
), (() =>
@initialize {}
command args
)

saveFile: (editor) ->
if editor.getURI()
Expand Down
19 changes: 17 additions & 2 deletions lib/idris-ide-mode.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,33 @@ class IdrisIdeMode extends EventEmitter
process: null
buffer: ''
idrisBuffers: 0
compilerOptions: {}

start: ->
start: (compilerOptions) ->
if (not @process?) || @process.killed
pathToIdris = atom.config.get("language-idris.pathToIdris")
@process = spawn pathToIdris, ['--ide-mode']
parameters =
if compilerOptions.options
['--ide-mode'].concat compilerOptions.options.split(' ')
else
['--ide-mode']
options =
if compilerOptions.src
cwd: compilerOptions.src
else
{}
@process =
spawn pathToIdris, parameters, options
@process.on 'error', @error
@process.on 'exit', @exited
@process.on 'close', @exited
@process.on 'disconnect', @exited

@process.stdout.setEncoding('utf8').on 'data', @stdout

setCompilerOptions: (options) ->
@compilerOptions options

send: (cmd) ->
Logger.logOutgoingCommand cmd
@process.stdin.write sexpFormatter.serialize(cmd)
Expand Down
39 changes: 34 additions & 5 deletions lib/idris-model.coffee
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
IdrisIdeMode = require './idris-ide-mode'
Logger = require './Logger'
Rx = require 'rx-lite'
JS = require './utils/js'
path = require 'path'

class IdrisModel
requestId: 0
ideModeRef: null
subjects: {}
warnings: {}
compilerOptions: {}
oldCompilerOptions: {}

ideMode: ->
if !@ideModeRef
ideMode: (compilerOptions) ->
if !@ideModeRef || !JS.objectEqual(@oldCompilerOptions, compilerOptions)
@ideModeRef = new IdrisIdeMode
@ideModeRef.on 'message', @handleCommand
@ideModeRef.start()
@ideModeRef.start compilerOptions
@oldCompilerOptions = compilerOptions
@ideModeRef

stop: ->
@ideModeRef?.stop()

setCompilerOptions: (options) ->
@compilerOptions = options

handleCommand: (cmd) =>
if cmd.length > 0
[op, params..., id] = cmd
Expand Down Expand Up @@ -62,11 +70,29 @@ class IdrisModel
subject = new Rx.Subject
@subjects[id] = subject
@warnings[id] = []
@ideMode().send [cmd, id]
@ideMode(@compilerOptions).send [cmd, id]
subject

changeDirectory: (dir) ->
@interpret ":cd #{dir}"

load: (uri) ->
@prepareCommand [':load-file', uri]
dir =
if @compilerOptions.src
@compilerOptions.src
else
path.dirname uri

cd =
if dir != @compilerOptions.src
@compilerOptions.src = dir
@changeDirectory dir
.map (_) -> dir
else
Rx.Observable.of dir

cd.flatMap (_) =>
@prepareCommand [':load-file', uri]

docsFor: (word) ->
@prepareCommand [':docs-for', word]
Expand All @@ -83,6 +109,9 @@ class IdrisModel
makeLemma: (line, word) ->
@prepareCommand [':make-lemma', line, word]

interpret: (code) ->
@prepareCommand [':interpret', code]

makeCase: (line, word) ->
@prepareCommand [':make-case', line, word]

Expand Down
63 changes: 63 additions & 0 deletions lib/utils/ipkg.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
path = require 'path'
fs = require 'fs'
Rx = require 'rx-lite'

optionsRegexp = /opts\s*=\s*\"([^\"]*)\"/
sourcedirRegexp = /sourcedir\s*=\s*([a-zA-Z/0-9.]+)/

# Find all ipkg-files in a directory and returns
# an observable of an array of files
findIpkgFile = (project) ->
directory = project.getDirectories()[0].path
readDir = Rx.Observable.fromNodeCallback fs.readdir

r = readDir directory
r
.map (files) ->
files
.map (file) ->
file: file
directory: directory
path: path.join directory, file
ext: path.extname file
.filter (file) ->
file.ext == '.ipkg'

parseIpkgFile = (fileInfo) ->
(fileContents) ->
optionsMatches = fileContents.match optionsRegexp
sourcedirMatches = fileContents.match sourcedirRegexp

compilerOptions = {}
if optionsMatches
compilerOptions.options = optionsMatches[1]

compilerOptions.src =
if sourcedirMatches
path.join fileInfo.directory, sourcedirMatches[1]
else
fileInfo.directory

compilerOptions

readIpkgFile = (ipkgFile) ->
readFile = Rx.Observable.fromNodeCallback fs.readFile
readFile ipkgFile.path,
encoding: 'utf8'

# Find the ipkg file in the top directory of the project and return
# the compiler options in it.
compilerOptions = (project) ->
ipkgFilesObserver = findIpkgFile project
ipkgFilesObserver.flatMap (ipkgFiles) ->
if ipkgFiles.length
ipkgFile = ipkgFiles[0]
readIpkgFile(ipkgFile)
.map parseIpkgFile(ipkgFile)
else
Rx.Observable.return {}

module.exports =
findIpkgFile: findIpkgFile
readIpkgFile: readIpkgFile
compilerOptions: compilerOptions
6 changes: 6 additions & 0 deletions lib/utils/js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# slow method to compare objects.
objectEqual = (a, b) ->
JSON.stringify(a) == JSON.stringify(b)

module.exports =
objectEqual: objectEqual
2 changes: 1 addition & 1 deletion lib/utils/sexp-formatter.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ isString = (s) ->
typeof(s) == 'string' || s instanceof String

isSymbol = (s) ->
isString(s) and s.length > 0 and s[0] == ':'
isString(s) and s.length > 0 and s[0] == ':' and s.indexOf(' ') == -1

isBoolean = (s) ->
typeof(s) == 'boolean' || s instanceof Boolean
Expand Down
8 changes: 8 additions & 0 deletions spec/parse-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ list6 =
6
]

test7 = "(:interpret \":cd C:/Path/to/dir\")"
list7 =
[
":interpret"
":cd C:/Path/to/dir"
]

describe "The sub-parser(s)", ->
it "for :True and :False should work.", ->
expect(runP(parse.trueP, ':True')).toEqual(true)
Expand Down Expand Up @@ -124,6 +131,7 @@ describe "A parser", ->
expect(sexpFormatter.formatSexp(list2)).toEqual(test2)
expect(sexpFormatter.formatSexp(list3)).toEqual(test3)
expect(sexpFormatter.formatSexp(list4)).toEqual(test4)
expect(sexpFormatter.formatSexp(list7)).toEqual(test7)

it "should serialize common commands.", ->
loadFile = [[':load-file', "idris.idr"], 1]
Expand Down

0 comments on commit 29bf15d

Please sign in to comment.