From a9fa16deb53a2a2caeecb9d0c94c94bcef562ff6 Mon Sep 17 00:00:00 2001 From: "B. Scott Michel" Date: Thu, 27 Apr 2017 16:45:49 -0700 Subject: [PATCH 1/4] Add cabal's "new build" rules - Add the cabal 'new build' rules - Make ^B really invoke SublimeHaskell's build command - Update pylint settings for better cross-platform compatibility --- Keymaps/Default.sublime-keymap | 1 + Settings/SublimeHaskell.sublime-settings | 10 ++++-- build.py | 45 +++++++++++++++++------- subhask.sublime-project | 5 ++- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/Keymaps/Default.sublime-keymap b/Keymaps/Default.sublime-keymap index 829353f7..259fe7bd 100644 --- a/Keymaps/Default.sublime-keymap +++ b/Keymaps/Default.sublime-keymap @@ -22,6 +22,7 @@ { "key": "scanned_source" } ], "command": "sublime_haskell_go_to_declaration" }, + { "keys": ["ctrl+b"], "context": [ { "key" : "haskell_source" } ], "command": "sublime_haskell_build" }, { "keys": ["ctrl+shift+y"], "context": [ { "key": "haskell_source" } ], "command": "sublime_haskell_expand_selection_expression" }, { "keys": ["ctrl+m", "ctrl+i"], "context": [ { "key": "haskell_source" } ], "command": "sublime_haskell_insert_import" }, { "keys": ["ctrl+k", "ctrl+h", "ctrl+t"], "context": [ { "key" : "haskell_source" } ], "command": "sublime_haskell_show_type" }, diff --git a/Settings/SublimeHaskell.sublime-settings b/Settings/SublimeHaskell.sublime-settings index 91a8e508..6f96c5d3 100644 --- a/Settings/SublimeHaskell.sublime-settings +++ b/Settings/SublimeHaskell.sublime-settings @@ -8,8 +8,14 @@ // Enable auto build project on save "enable_auto_build": false, - // Haskell build tool: "cabal" to use only cabal and "stack" to use stack whenever - // there's stack.yaml in project root + // Haskell build system. Choices are: + // + // "cabal": Use cabal to build projects + // "cabal-new-build": Use the new cabal build system + // "stack" Use stack to build projects + // + // Ideally, you should put your default preference in your User preferences. Someday, SublimeHaskell + // will pick this up on a per-project basis. "haskell_build_tool": "stack", // Enable auto run tests on save, has effect only if auto build enabled (enable_auto_build) diff --git a/build.py b/build.py index d59770fd..d589a5b4 100644 --- a/build.py +++ b/build.py @@ -18,12 +18,17 @@ BUILD_TOOL = { 'cabal': {'command': 'cabal', 'name': 'cabal'}, + 'cabal-new-build': {'command': 'cabal', 'name': 'cabal (new build)'}, 'stack': {'command': 'stack', 'name': 'stack'} } def same_steps(steps): - return {'cabal': steps, 'stack': steps} + return { + 'cabal': steps, + 'cabal-new-build': steps, + 'stack': steps + } BUILD_TOOL_CONFIG = { 'clean': { @@ -33,17 +38,26 @@ def same_steps(steps): 'configure': { 'message': 'Configuring', 'steps': { - 'cabal': [['configure', '--enable-tests']], - 'stack': [] + 'cabal': [['configure', '--enable-tests']], + 'cabal-new-build': [['new-configure', '--enable-tests']], + 'stack': [] } }, 'build': { 'message': 'Building', - 'steps': same_steps([['build']]) + 'steps': { + 'cabal': [['build']], + 'cabal-new-build': [['new-build']], + 'stack': [['build']] + } }, 'typecheck': { 'message': 'Checking', - 'steps': same_steps([['build', '--ghc-options=-c']]) + 'steps': { + 'cabal': [['build', '--ghc-options=-c']], + 'cabal-new-build': [['new-build', '--ghc-options=-c']], + 'stack': [['build', '--ghc-options=-c']] + } }, # Commands with warnings: # Run fast, incremental build first. Then build everything with -Wall and -fno-code @@ -51,30 +65,35 @@ def same_steps(steps): 'build_then_warnings': { 'message': 'Building', 'steps': { - 'cabal': [['build'], ['build', '-v0', '--ghc-options=-fforce-recomp -fno-code']], - 'stack': [['build']] + 'cabal': [['build'], ['build', '-v0', '--ghc-options=-fforce-recomp -fno-code']], + 'cabal-new-build': [['new-build'], ['new-build', '-v0', '--ghc-options=-fforce-recomp -fno-code']], + 'stack': [['build']] } }, 'typecheck_then_warnings': { 'message': 'Checking', 'steps': { - 'cabal': [['build', '--ghc-options=-c'], ['build', '-v0', '--ghc-options=-fforce-recomp -fno-code']], - 'stack': [['build']] + 'cabal': [['build', '--ghc-options=-c'], ['build', '-v0', '--ghc-options=-fforce-recomp -fno-code']], + 'cabal-new-build': [['new-build', '--ghc-options=-c'], + ['new-build', '-v0', '--ghc-options=-fforce-recomp -fno-code']], + 'stack': [['build']] } }, 'rebuild': { 'message': 'Rebuilding', 'steps': { - 'cabal': [['clean'], ['configure', '--enable-tests'], ['build']], - 'stack': [['clean'], ['build']] + 'cabal': [['clean'], ['configure', '--enable-tests'], ['build']], + 'cabal-new-build': [['clean'], ['new-configure', '--enable-tests'], ['new-build']], + 'stack': [['clean'], ['build']] } }, 'install': { 'message': 'Installing', 'steps': { - 'cabal': [['install', '--enable-tests']], - 'stack': [['install']] + 'cabal': [['install', '--enable-tests']], + 'cabal-new-build': [['install', '--enable-tests']], + 'stack': [['install']] } }, 'test': { diff --git a/subhask.sublime-project b/subhask.sublime-project index bf4177c4..86a43754 100644 --- a/subhask.sublime-project +++ b/subhask.sublime-project @@ -11,9 +11,8 @@ "linters": { "pylint" : { "paths": [ - "$USERPROFILE\\Sublime", - "$USERPROFILE\\Sublime\\Data\\Packages", - "$HOME/Library/Application Support/Sublime Text 3/Packages/", + "${env:USERPROFILE}\\Sublime", + "${sublime}", "/Applications/Sublime Text.app/Contents/MacOS" ], } From f4f56a75232e3c2494bc4c9cd983591d89c1aeab Mon Sep 17 00:00:00 2001 From: "B. Scott Michel" Date: Fri, 28 Apr 2017 11:54:03 -0700 Subject: [PATCH 2/4] more issue fixes - Output window after a build completes now reappears. - Removed maximum hsdev version number. It didn't really make much sense, when I thought about it for a while. - Patch hsdev command line arguments (and add a new setting) to support simple-log's simplified "--log-level". --- Settings/SublimeHaskell.sublime-settings | 14 ++++-- hsdev/agent.py | 59 +++++++++++++++--------- internals/output_collector.py | 6 ++- internals/settings.py | 2 + parseoutput.py | 9 ++-- 5 files changed, 60 insertions(+), 30 deletions(-) diff --git a/Settings/SublimeHaskell.sublime-settings b/Settings/SublimeHaskell.sublime-settings index 6f96c5d3..b556ccef 100644 --- a/Settings/SublimeHaskell.sublime-settings +++ b/Settings/SublimeHaskell.sublime-settings @@ -161,7 +161,15 @@ // Log level, 0 — no log, 1 — errors, 2 — warnings, 3 — info messages, 4 — debug, 5 — trace "log": 1, - // hsdev's logging: 'use default' (somewhat verbose, informational), 'use silent' (quiet) - // This is a somewhat magic setting that comes from the Haskell 'simple-log' documentation. - "hsdev_log_config": "use silent" + // hsdev logging output: + // + // !!!PAY ATTENTION TO YOUR HSDEV VERSION!!! + // + // hsdev version 0.2.2.0 and below: The "hsdev_log_config" preference controls hsdev's logging. It has several interesting + // values: 'use default' (somewhat verbose, informational), 'use silent' (quiet) + // + // hsdev version 0.2.3.0 and higher: The "hsdev_log_level" preference controls hsdev's logging. Valid values are + // 'trace', 'debug', 'info', 'warning', 'error' and 'fatal'. + "hsdev_log_config": "use silent", + "hsdev_log_level": "warning" } diff --git a/hsdev/agent.py b/hsdev/agent.py index 94001260..8be8298a 100644 --- a/hsdev/agent.py +++ b/hsdev/agent.py @@ -22,7 +22,6 @@ HSDEV_DEFAULT_PORT = 4567 HSDEV_MIN_VER = [0, 2, 0, 0] # minimum hsdev version -HSDEV_MAX_VER = [0, 2, 3, 0] # maximum hsdev version def hsdev_version(): retval = None @@ -47,8 +46,12 @@ def show_version(ver): return '.'.join(map(str, ver)) -def check_version(ver, minimal, maximal): - return ver is not None and ver >= minimal and (not maximal or ver < maximal) +def check_version(ver, minimal): + return ver is not None and ver >= minimal + + +def patch_simple_log(ver): + return ver >= [0, 2, 3, 1] def format_error_details(details): @@ -99,7 +102,7 @@ class HsDevProcess(threading.Thread): `stderr` file objects -- `hsdev` can be quite chatty, which leads to deadlock when `hsdev` has filled the pipe between SublimeText and itself. """ - def __init__(self, port=HSDEV_DEFAULT_PORT, cache=None, log_file=None, log_config=None): + def __init__(self, port, version, cache=None, log_file=None, log_config=None): super().__init__() self.process = None self.drain_stdout = None @@ -112,13 +115,17 @@ def __init__(self, port=HSDEV_DEFAULT_PORT, cache=None, log_file=None, log_confi self.cache = cache self.log_file = log_file self.log_config = log_config + self.version = version def run(self): + new_simple_log = patch_simple_log(self.version) + cmd = concat_args([(True, ["hsdev", "run"]), (self.port, ["--port", str(self.port)]), (self.cache, ["--cache", self.cache]), (self.log_file, ["--log", self.log_file]), - (self.log_config, ["--log-config", self.log_config])]) + (not new_simple_log and self.log_config, ["--log-config", self.log_config]), + (new_simple_log and self.log_config, ["--log-level", self.log_config])]) Logging.log('hsdev command: {0}'.format(cmd), Logging.LOG_DEBUG) @@ -130,7 +137,7 @@ def run(self): hsdev_proc = ProcHelper.ProcHelper(cmd) if hsdev_proc.process is None: Logging.log('Failed to create hsdev process', Logging.LOG_ERROR) - return None + return # Use TextIOWrapper here because it combines decoding with newline handling, # which means less to maintain. @@ -139,11 +146,15 @@ def run(self): while True: srvout = hsdev_proc.process.stdout.readline().strip() - Logging.log('hsdev initial output: {0}'.format(srvout), Logging.LOG_DEBUG) - start_confirm = re.match(r'^.*?hsdev> Server started at port (?P\d+)$', srvout) - if start_confirm: - Logging.log('hsdev server started at port {0}'.format(start_confirm.group('port'))) - break + if srvout != '': + Logging.log('hsdev initial output: {0}'.format(srvout), Logging.LOG_DEBUG) + start_confirm = re.match(r'[Ss]erver started at port (?P\d+)$', srvout) + if start_confirm: + Logging.log('hsdev server started at port {0}'.format(start_confirm.group('port'))) + break + else: + Logging.log('hsdev initial output: Got EOF. Server not started successfully.') + return self.process = hsdev_proc.process self.drain_stdout = OutputCollector.DescriptorDrain('hsdev stdout', self.process.stdout) @@ -157,6 +168,7 @@ def run(self): self.drain_stdout.stop() self.drain_stderr.stop() HsCallback.call_callback(self.on_exit, name='HsDevProcess.on_exit') + self.stop_event.clear() def active(self): @@ -475,17 +487,12 @@ class HsDevLocalAgent(HsDevAgent): def __init__(self, port): super().__init__("localhost", port) - self.hsdev_process = HsDevProcess(cache=os.path.join(Common.sublime_haskell_cache_path(), 'hsdev'), - log_file=os.path.join(Common.sublime_haskell_cache_path(), 'hsdev', 'hsdev.log'), - log_config=Settings.PLUGIN.hsdev_log_config) - - def do_start_hsdev(self): hsdev_ver = hsdev_version() if hsdev_ver is None: Common.output_error_async(sublime.active_window(), "\n".join(HsDevLocalAgent.hsdev_not_found)) return False - elif not check_version(hsdev_ver, HSDEV_MIN_VER, HSDEV_MAX_VER): + elif not check_version(hsdev_ver, HSDEV_MIN_VER): Common.output_error_async(sublime.active_window(), "\n".join(HsDevLocalAgent.hsdev_wrong_version(hsdev_ver))) return False else: @@ -502,12 +509,22 @@ def connected_(): self.client.link() self.client.set_on_connected(connected_) + + hsdev_log_settings = Settings.PLUGIN.hsdev_log_config + if patch_simple_log(hsdev_ver): + hsdev_log_settings = Settings.PLUGIN.hsdev_log_level + + self.hsdev_process = HsDevProcess(port, hsdev_ver, + cache=os.path.join(Common.sublime_haskell_cache_path(), 'hsdev'), + log_file=os.path.join(Common.sublime_haskell_cache_path(), 'hsdev', 'hsdev.log'), + log_config=hsdev_log_settings) self.hsdev_process.on_start = start_ self.hsdev_process.on_exit = exit_ - self.hsdev_process.start() - self.hsdev_process.create() - return True + def do_start_hsdev(self): + self.hsdev_process.start() + self.hsdev_process.create() + return True def do_stop_hsdev(self): @@ -519,7 +536,7 @@ def do_stop_hsdev(self): @staticmethod def hsdev_wrong_version(version_str): return ["SublimeHaskell: hsdev version is incorrect: {0}".format(show_version(version_str)), - "Required version: >= {0} and < {1}".format(show_version(HSDEV_MIN_VER), show_version(HSDEV_MAX_VER)), + "Required version: >= {0}".format(show_version(HSDEV_MIN_VER)), "Update it by running 'cabal update' and 'cabal install hsdev'", "", "To supress this message and disable hsdev set 'enable_hsdev' to false" diff --git a/internals/output_collector.py b/internals/output_collector.py index 915aa83f..034f30de 100644 --- a/internals/output_collector.py +++ b/internals/output_collector.py @@ -106,7 +106,11 @@ def run(self): if isinstance(line, bytearray): line = Utils.decode_bytes(line) line = line.rstrip() - print('<{0}> {1}'.format(self.label, line)) + if line != '': + print('<{0}> {1}'.format(self.label, line)) + else: + # Got EOF. Stop. + self.stop() def stop(self): self.stop_me.set() diff --git a/internals/settings.py b/internals/settings.py index a657b8e8..25990099 100644 --- a/internals/settings.py +++ b/internals/settings.py @@ -58,6 +58,7 @@ class SetttingsContainer(object): 'hsdev_host': ('hsdev_host', 'localhost'), 'hsdev_local_process': ('hsdev_local_process', True), 'hsdev_log_config': ('hsdev_log_config', 'use silent'), + 'hsdev_log_level': ('hsdev_log_level', 'warning'), 'hsdev_port': ('hsdev_port', 4567), 'inhibit_completions': ('inhibit_completions', False), 'inspect_modules': ('inspect_modules', True), @@ -93,6 +94,7 @@ def __init__(self): self.hsdev_host = None self.hsdev_local_process = None self.hsdev_log_config = None + self.hsdev_log_level = None self.hsdev_port = None self.inhibit_completions = None self.inspect_modules = None diff --git a/parseoutput.py b/parseoutput.py index 8383ce87..9b7c03e9 100644 --- a/parseoutput.py +++ b/parseoutput.py @@ -182,9 +182,8 @@ def show_output_result_text(view, msg, text, exit_code, base_dir): Common.show_status_message_process(msg, success) # Show panel if there is any text to show (without the part that we add) - if text: - if Settings.PLUGIN.show_error_window: - sublime.set_timeout(lambda: write_output(view, output, base_dir), 0) + if text and Settings.PLUGIN.show_error_window: + sublime.set_timeout(lambda: write_output(view, output, base_dir), 0) def parse_output_messages_and_show(view, msg, base_dir, exit_code, stderr): @@ -364,13 +363,13 @@ def mark_messages_in_view(messages, view): sublime.HIDDEN) -def write_output(view, text, cabal_project_dir, panel_display=False): +def write_output(view, text, cabal_project_dir, panel_out=True): "Write text to Sublime's output panel." global ERROR_VIEW ERROR_VIEW = Common.output_panel(view.window(), text, panel_name=OUTPUT_PANEL_NAME, syntax='HaskellOutputPanel', - panel_display=panel_display) + panel_display=panel_out) ERROR_VIEW.settings().set("RESULT_FILE_REGEX", RESULT_FILE_REGEX) ERROR_VIEW.settings().set("result_base_dir", cabal_project_dir) From 53d4f3fdae30abc0798c53a70692c1cf480dbe02 Mon Sep 17 00:00:00 2001 From: "B. Scott Michel" Date: Fri, 28 Apr 2017 13:07:30 -0700 Subject: [PATCH 3/4] and the local hsdev exits properly --- hsdev/agent.py | 43 ++++++++++++++++++++++++++----------------- hsdev/client.py | 4 +++- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/hsdev/agent.py b/hsdev/agent.py index 8be8298a..eda4fba4 100644 --- a/hsdev/agent.py +++ b/hsdev/agent.py @@ -124,6 +124,14 @@ def run(self): (self.port, ["--port", str(self.port)]), (self.cache, ["--cache", self.cache]), (self.log_file, ["--log", self.log_file]), + # HACK! HACK! HACK! Alexandr changed simple-log's command line API. + # + # "--log-config" only applies to earlier hsdev versions that support 'log politics' (and for those + # of us old enough to remember the Soviet era, 'log politics' is an incredibly Soviet notion that + # makes us smile! :-) + # + # Versions 0.2.3.0 and later: Simplified argument -- just the log level, and only one log level, + # Yvgeny! (With apologies to "The Hunt for Red October".) (not new_simple_log and self.log_config, ["--log-config", self.log_config]), (new_simple_log and self.log_config, ["--log-level", self.log_config])]) @@ -487,15 +495,25 @@ class HsDevLocalAgent(HsDevAgent): def __init__(self, port): super().__init__("localhost", port) + self.hsdev_process = None hsdev_ver = hsdev_version() if hsdev_ver is None: Common.output_error_async(sublime.active_window(), "\n".join(HsDevLocalAgent.hsdev_not_found)) - return False elif not check_version(hsdev_ver, HSDEV_MIN_VER): Common.output_error_async(sublime.active_window(), "\n".join(HsDevLocalAgent.hsdev_wrong_version(hsdev_ver))) - return False else: + hsdev_log_settings = Settings.PLUGIN.hsdev_log_config + if patch_simple_log(hsdev_ver): + hsdev_log_settings = Settings.PLUGIN.hsdev_log_level + + self.hsdev_process = HsDevProcess(port, hsdev_ver, + cache=os.path.join(Common.sublime_haskell_cache_path(), 'hsdev'), + log_file=os.path.join(Common.sublime_haskell_cache_path(), 'hsdev', 'hsdev.log'), + log_config=hsdev_log_settings) + + def do_start_hsdev(self): + if self.hsdev_process is not None: def start_(): Logging.log('hsdev process started', Logging.LOG_TRACE) self.connect_clients() @@ -505,26 +523,17 @@ def exit_(): self.disconnect_clients() def connected_(): - Logging.log('hsdev agent: primary connection to hsdev established', Logging.LOG_TRACE) + Logging.log('hsdev agent: primary connection to hsdev established', Logging.LOG_DEBUG) self.client.link() - self.client.set_on_connected(connected_) - - hsdev_log_settings = Settings.PLUGIN.hsdev_log_config - if patch_simple_log(hsdev_ver): - hsdev_log_settings = Settings.PLUGIN.hsdev_log_level - - self.hsdev_process = HsDevProcess(port, hsdev_ver, - cache=os.path.join(Common.sublime_haskell_cache_path(), 'hsdev'), - log_file=os.path.join(Common.sublime_haskell_cache_path(), 'hsdev', 'hsdev.log'), - log_config=hsdev_log_settings) self.hsdev_process.on_start = start_ self.hsdev_process.on_exit = exit_ + self.client.set_on_connected(connected_) - def do_start_hsdev(self): - self.hsdev_process.start() - self.hsdev_process.create() - return True + self.hsdev_process.start() + self.hsdev_process.create() + + return self.hsdev_process is not None def do_stop_hsdev(self): diff --git a/hsdev/client.py b/hsdev/client.py index f38ea8cc..61fd3ff5 100644 --- a/hsdev/client.py +++ b/hsdev/client.py @@ -247,7 +247,9 @@ def get_response(self): @HsDecorator.command def link(self, hold=False): - return cmd('link', {'hold': hold}) + retval = cmd('link', {'hold': hold}) + Logging.log("link command sent, result: {0}".format(retval), Logging.LOG_DEBUG) + return retval @HsDecorator.command def ping(self): From 580a87c35de1a2712204f142f96e85d08efa7949 Mon Sep 17 00:00:00 2001 From: "B. Scott Michel" Date: Fri, 28 Apr 2017 13:42:47 -0700 Subject: [PATCH 4/4] 1.2.2 release message. --- Messages/1.2.2.md | 135 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 Messages/1.2.2.md diff --git a/Messages/1.2.2.md b/Messages/1.2.2.md new file mode 100644 index 00000000..02c223c1 --- /dev/null +++ b/Messages/1.2.2.md @@ -0,0 +1,135 @@ +SublimeHaskell 1.2.2 +==================== + +__PLEASE RESTART SublimeText AFTER INSTALLING THIS UPDATE!__ + +### 1.2.2 + +- Make the parsed error/warning output window reappear after a build. (Reported by _cartazio_.) + +- _Ctrl-B_ invokes `SublimeHaskell: Build`. No more spurious error messages about `runhaskell`. (Updated Mac OS X keymap is in the works so that _Cmd-B_ does the right thing too. Please have some patience.) + +- Bump the `hsdev` version number _and_ patch the command line to reflect changes to the `simple-log` package's updated (simplified) command line arguments. __Please read the SublimeHaskell package's default settings if you want or need to adjust the `hsdev` logging level.__ + +- Add support for the new `cabal` build system (see the _haskell_build_tool_ setting.) The builder's name is `cabal-new-build`. Eventually, this will become `cabal` when the `cabal` maintainers decide their new build system is stable. + + +### 1.2.1 + +- Fix `client.py` tracebacks and `hsdev` connection sequencing. Connections are now synchronous, not asynchronous. + +### 1.2.0 + +_SublimeHaskell_ 1.2.0 is a stability release, with a lot of fixes resulting from a `pylint` code audit. While a majority of these "fixes" were Python style-related (line lengths, spaces between keywords and arguments), there were errors (usually bad attribute references to the wrong imported module) and a need to simplify complex logic in a few places (breaking up complex `if`-`elif`-`else` blocks.) The overall McCabe assessment went from 5.75/10 to 9.71/10, a significant improvement. + +## `hsdev` 0.2.3.0 and higher + +The minimum supported `hsdev` is version 0.2.0.0. There is no upper bound on `hsdev`'s version; the current version (as of this release) is 0.2.3.2. + +Changes to the `simple-log` package simplified logging support. However, this simplification is a _breaking change_ to `hsdev` command line arguments. + +`hsdev` 0.2.3.0 and higher: Logging is controlled by the `hsdev_log_level` preference. Valid values are _trace_, _debug_, _info_, _warning_, _error_ and _fatal_. This sets the highest level of message that `simple-log` will output, i.e. _fatal_ will only output fatal messages, whereas _debug_ will output debug, info, warning, error and fatal messages. + +`hsdev` 0.2.2.9 and lower: Logging is still controlled by the `hsdev_log_config` preference. Eventually, this preferece will disappear when the `hsdev` minimum version is increased to 0.2.3.0. + +## SublimeText 2 Support Removed + +SublimeText 2 compatibility was tested after the `pylint` code audit. The current code base uses API calls to the SublimeText3 API that aren't present or featured in SublimeText2, and no workarounds exist. Couple that with the complete lack of issues related to SublimeText 2 and it wasn't a hard decision to remove SublimeText 2 compatibility. + +SublimeText 2: We loved you. We bid you a fond farewell as you head to your retirement. + +## New Feature: Remote `hsdev` + +The `hsdev` backend has three new preferences that control _SublimeHaskell_'s interaction with the `hsdev` backend: + +| Preference | Description | +| --------------------- |-----------------------------------------------------------------------| +| `hsdev_host` | The host name where the `hsdev` backend is located. | +| | Default: "localhost" | +| `hsdev_port` | The port number to which the `hsdev` backend listens for connections. | +| | Default: 4567 | +| `hsdev_local_process` | Controls whether _SublimeHaskell_ will start up a local `hsdev` backend process or simply connect to the backend via `hsdev_host` and `hsdev_port`. | +| | Default: true (`hsdev` backend process started by _SublimeHaskell_) | + +Add and customize the folowing SublimeHaskell_ "Settings - User" preferences if you have a remote `hsdev` backend that you want to use instead of having _SublimeHaskell_ start up its own `hsdev`: + +```yaml +{ + // Remote hsdev is on the "my_hsdev_host.domain.net" machine + "hsdev_host": "my_hsdev_host.domain.net", + // Remote hsdev listens to port 4096 for connections (default: 4567) + "hsdev_port": 4096, + // SublimeHaskell will not start or manage its own hsdev backend process + "hsdev_local_process": false, + + // Other preferences follow... +} +``` + +## Next/Previous Error + +These commands now work as intended. "Goto next error" didn't; it would get stuck at the first error. + +The _<alt>-D <alt>-E_ key mapping for "next error" and _<shift>-<alt>-D <shift>-<alt>-E_ mapping for "previous error" can be a bit awkward. This key mapping was borrowed from the (now defunct) SublimeClang plugin. If you prefer _<ctrl>-K n_ and _<ctrl>-K p_ that SublimeLinter uses, add these two lines to your "Key Bindings" preferences: + +``` + { "keys": ["ctrl+k", "n"], "context": [ { "key": "haskell_source" } ], "command": "sublime_haskell_next_error" }, + { "keys": ["ctrl+k", "p"], "context": [ { "key": "haskell_source" } ], "command": "sublime_haskell_previous_error" }, + +``` + +## In Case You Missed It... + +### Environment variables in `add_to_PATH` + +You may have missed this in an earlier release: You can use environment variables in the `add_to_PATH` setting. Both Unix-style (`$HOME`) and Windows-style (`%APPDATA%`) are supported. Tilde-expansion (`~/.local/bin`) also works. + +### `add_standard_dirs` Setting + +_SublimeHaskell_ will add several "standard" directories to its `PATH` when searching for tools. This eliminates the need to modify the `add_to_PATH` setting if you installed `hsdev` or `ghc-mod` in one of the "standard" Haskell places. `add_standard_dirs` defaults to `true`. + +Excerpt from the default setings: + +```yaml + // Add the "standard" directories where cabal and stack install executables, + // if they exist, in the following order (top to bottom): + // + // stack's install dir + // user bindir from $HOME/.cabal/config (*nix) or %APPDATA%/cabal/config (WinXX) + // global bindir from $HOME/.cabal/config (*nix) or %APPDATA%/cabal/config (WinXX) + // + // The defaults are: + // + // stack: {$HOME/.local|%APPDATA%/local}/bin + // user: {$HOME/.cabal|%APPDATA%/cabal}/bin + // global: {/usr/local|%PROGRAMFILES%/Haskell}/bin + "add_standard_dirs": true, +``` + +Basically, this should make it easier to use _SublimeHaskell_ out-of-the-box. + +## Upcoming Development Milestones + +Upcoming development milestones, listed in order of priority: + +### Backend Refactoring (Expected: 1.4.0) + +Work is in progress to refactor _SublimeHaskell_'s interaction with the three backends: `hsdev`, `ghc-mod` and `hdevtools`. Support for `hdevtools` may be dropped completely -- if `hdevtools` is important to you, please file an issue. The rationale for this refactoring is to make the interface cleaner between the plugin and the backend so that it's clear what features the backend supports and provide a reasonable default action for unsupported features. This will also enable enterprising Haskell backend developers to easily add new backends to _SublimeHaskell_ (e.g., `intero` support has come up a few times as an enhancement request.) + +__Note__: The refactored backend will introduce __BREAKING CHANGES__ to the default and user settings. + +### Lazy Backend Startup (Expected: 1.4.0) + +Related to backend refactoring is lazy backend startup: _SublimeHaskell_ should start up and connect to a backend only when it's needed. Currently, _SublimeHaskell_ is too eager and starts up the `hsdev` backend when the plugin is loaded. This enhancement will make _SublimeHaskell_ lazier or, at least, less eager. + +### Switching between multiple `stack.yaml`-s (Expected: 1.3.0) + +For `stack` tool users, it will be possible to specify a list of `stack.yaml` files in your project settings and switch between them. When you switch, _SublimeHaskell_ will execute a rebuild (`clean` followed by `build`). + +### The SublimeHaskell Editing Guide (Ongoing) + +Documentation is always a good thing. The guide is a work-in-progress. + +### SublimeText 3 Syntax Files (Expected: 1.5.0) + +There are numerous issues related to syntax highlighting. Syntax highlighting in SublimeText 2 was driven by collections of regular expressions. SublimeText 3 syntax highlighting still uses regexps and offers the ability to be more stateful. However, transitioning from _SublimeHaskell_'s improved ST2 syntax highlighting to ST3 syntax highlighting requires substantial work.