diff --git a/lib/completion-backend/completion-backend.coffee b/lib/completion-backend/completion-backend.coffee index 6517e81..4bcbffb 100644 --- a/lib/completion-backend/completion-backend.coffee +++ b/lib/completion-backend/completion-backend.coffee @@ -343,7 +343,7 @@ class CompletionBackend position = Range.fromPointWithDelta(position, 0, 0) if position? prefix = prefix.slice 1 if prefix.startsWith '_' @process.getTypeInBuffer(buffer, position).then ({type}) => - @getSymbolsForBuffer(buffer).then (symbols) -> + @getSymbolsForBuffer(buffer).then (symbols) => ts = symbols.filter (s) -> return false unless s.typeSignature? tl = s.typeSignature.split(' -> ').slice(-1)[0] @@ -351,8 +351,18 @@ class CompletionBackend ts = tl.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&") rx = RegExp ts.replace(/\b[a-z]\b/g, '.+'), '' rx.test(type) - if prefix.length is 0 - ts.sort (a, b) -> - FZ.score(b.typeSignature, type) - FZ.score(a.typeSignature, type) - else - FZ.filter ts, prefix, key: 'qname' + ts2 = + if prefix.length is 0 + ts.sort (a, b) -> + FZ.score(b.typeSignature, type) - FZ.score(a.typeSignature, type) + else + FZ.filter ts, prefix, key: 'qname' + @process.doHoleFill(buffer, position).then ({suggestions}) -> + return ts2 unless suggestions? + ts2.unshift (suggestions.map (text) -> + name: text + qname: text + typeSignature: type + symbolType: 'snippet' + )... + return ts2 diff --git a/lib/ghc-mod/ghc-modi-process-real.coffee b/lib/ghc-mod/ghc-modi-process-real.coffee index ba102fd..d4d0382 100644 --- a/lib/ghc-mod/ghc-modi-process-real.coffee +++ b/lib/ghc-mod/ghc-modi-process-real.coffee @@ -3,6 +3,7 @@ CP = require('child_process') InteractiveProcess = require './interactive-process' {debug, warn, mkError, withTempFile, EOT} = Util = require '../util' {EOL} = require('os') +_ = require 'underscore-plus' module.exports = class GhcModiProcessReal @@ -10,7 +11,10 @@ class GhcModiProcessReal @disposables = new CompositeDisposable @disposables.add @emitter = new Emitter - run: ({interactive, command, text, uri, dashArgs, args, suppressErrors}) -> + run: ({interactive, command, text, uri, dashArgs, args, suppressErrors, timeout}) -> + if timeout? and interactive + throw new Error('Can not have interactive action with set timeout! This + is an error in haskell-ghc-mod. Please report it.') args ?= [] dashArgs ?= [] if atom.config.get('haskell-ghc-mod.lowMemorySystem') @@ -25,9 +29,9 @@ class GhcModiProcessReal P = if text? and not @caps.fileMap withTempFile text, uri, (tempuri) -> - fun {command, uri: tempuri, args} + fun {command, uri: tempuri, args, timeout} else - fun {command, text, uri, args} + fun {command, text, uri, args, timeout} P.catch (err) => debug err if err.name is 'InteractiveActionTimeout' @@ -44,6 +48,10 @@ class GhcModiProcessReal """ stack: err.stack dismissable: true + return [] + else if err.name is 'NonInteractiveActionTimeout' + warn err + return [] else if not suppressErrors atom.notifications.addFatalError " Haskell-ghc-mod: ghc-mod @@ -77,7 +85,7 @@ class GhcModiProcessReal @proc = null return @proc - runModCmd: ({command, text, uri, args}) => + runModCmd: ({command, text, uri, args, timeout}) => modPath = atom.config.get('haskell-ghc-mod.ghcModPath') result = [] err = [] @@ -88,7 +96,7 @@ class GhcModiProcessReal if text? cmd = ['--map-file', uri].concat cmd stdin = "#{text}#{EOT}" if text? - Util.execPromise modPath, cmd, @options, stdin + Util.execPromise modPath, cmd, _.extend({timeout}, @options), stdin .then (stdout) -> stdout.split(EOL).slice(0, -1).map (line) -> line.replace /\0/g, '\n' diff --git a/lib/ghc-mod/ghc-modi-process.coffee b/lib/ghc-mod/ghc-modi-process.coffee index 7f0a4ab..61ff836 100644 --- a/lib/ghc-mod/ghc-modi-process.coffee +++ b/lib/ghc-mod/ghc-modi-process.coffee @@ -338,6 +338,28 @@ class GhcModiProcess ] replacement: text + doHoleFill: (buffer, crange) => + return Promise.resolve [] unless buffer.getUri()? + crange = Util.tabShiftForRange(buffer, crange) + @queueCmd 'typeinfo', + interactive: false + buffer: buffer + command: 'auto' + uri: buffer.getUri() + text: buffer.getText() if buffer.isModified() + args: [crange.start.row + 1, crange.start.column + 1] + timeout: 5000 # TODO: Make configurable + .then (lines) -> + return {} if lines.length == 0 or lines[1] == "" + + [line_, rowstart, colstart, rowend, colend, text] = lines[0].match(/^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/) + + range: Range.fromObject [ + [parseInt(rowstart) - 1, parseInt(colstart) - 1], + [parseInt(rowend) - 1, parseInt(colend) - 1] + ] + suggestions: lines[1..] + doSigFill: (buffer, crange) => return Promise.resolve [] unless buffer.getUri()? crange = Util.tabShiftForRange(buffer, crange) diff --git a/lib/util.coffee b/lib/util.coffee index 8cda265..d64df6f 100644 --- a/lib/util.coffee +++ b/lib/util.coffee @@ -58,6 +58,8 @@ module.exports = Util = Util.warn("Running #{cmd} #{args} failed with ", error) Util.warn stdout if stdout error.stack = (new Error).stack + if child.killed and opts.timeout? + error.name = 'NonInteractiveActionTimeout' reject error else Util.debug "Got response from #{cmd} #{args}", stdout: stdout, stderr: stderr