Skip to content

Commit

Permalink
Fixed column calc
Browse files Browse the repository at this point in the history
  • Loading branch information
mvoidex committed Mar 16, 2013
1 parent 4f21484 commit 7fb3078
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 11 deletions.
10 changes: 7 additions & 3 deletions CabalInspector.hs
Expand Up @@ -12,12 +12,14 @@ import Distribution.PackageDescription.Parse
import qualified System.Environment as Environment

data CabalInfo = CabalInfo {
cabalExecutables :: [CabalExecutable] }
cabalExecutables :: [CabalExecutable],
cabalSourceDirs :: [FilePath] }
deriving (Show)

instance Json.ToJSON CabalInfo where
toJSON info = Json.object [
"executables" .= cabalExecutables info]
"executables" .= cabalExecutables info,
"source-dirs" .= cabalSourceDirs info]

data CabalExecutable = CabalExecutable {
executableName :: String,
Expand All @@ -31,7 +33,9 @@ instance Json.ToJSON CabalExecutable where

analyzeCabal :: String -> Either String CabalInfo
analyzeCabal source = case parsePackageDescription source of
ParseOk _ r -> Right $ CabalInfo $ map (uncurry CabalExecutable . second (exeName . condTreeData)) $ condExecutables r
ParseOk _ r -> Right CabalInfo {
cabalExecutables = map (uncurry CabalExecutable . second (exeName . condTreeData)) $ condExecutables r,
cabalSourceDirs = maybe [] (hsSourceDirs . libBuildInfo . condTreeData) $ condLibrary r }
ParseFailed e -> Left $ "Parse failed: " ++ show e

main :: IO ()
Expand Down
2 changes: 1 addition & 1 deletion autocomplete.py
Expand Up @@ -992,7 +992,7 @@ def _refresh_module_info(self, filename, standalone = True):
ghc_opts_args = [' '.join(ghc_opts)] if ghc_opts else []

exit_code, stdout, stderr = call_and_wait(
[MODULE_INSPECTOR_EXE_PATH, filename] + ghc_opts_args, cwd = get_cwd(filename))
[MODULE_INSPECTOR_EXE_PATH, filename] + ghc_opts_args, cwd = get_source_dir(filename))

module_inspector_out = MODULE_INSPECTOR_RE.search(stdout)

Expand Down
36 changes: 33 additions & 3 deletions haskell_type.py
Expand Up @@ -12,6 +12,7 @@
from SublimeHaskell.autocomplete import autocompletion
from SublimeHaskell.hdevtools import hdevtools_type
from SublimeHaskell.ghcmod import ghcmod_type
from functools import reduce

# Used to find out the module name.
MODULE_RE_STR = r'module\s+([^\s\(]*)' # "module" followed by everything that is neither " " nor "("
Expand All @@ -24,7 +25,6 @@
# Name of the sublime panel in which type information is shown.
TYPE_PANEL_NAME = 'haskell_type_panel'


def parse_ghc_mod_type_line(l):
"""
Returns the `groupdict()` of GHCMOD_TYPE_LINE_RE matching the given line,
Expand All @@ -33,17 +33,44 @@ def parse_ghc_mod_type_line(l):
match = GHCMOD_TYPE_LINE_RE.match(l)
return match and match.groupdict()

def tabs_offset(view, point):
"""
Returns count of '\t' before point in line multiplied by 7
8 is size of type as supposed by ghc-mod, to every '\t' will add 7 to column
Subtract this value to get sublime column by ghc-mod column, add to get ghc-mod column by sublime column
"""
cur_line = view.substr(view.line(point))
return len(filter(lambda ch: ch == '\t', cur_line)) * 7

def sublime_column_to_type_column(view, line, column):
cur_line = view.substr(view.line(view.text_point(line, column)))
return column + len(filter(lambda ch: ch == '\t', cur_line)) * 7 + 1

def type_column_to_sublime_column(view, line, column):
cur_line = view.substr(view.line(view.text_point(line - 1, 0)))
col = 1
real_col = 0
for c in cur_line:
col += (8 if c == '\t' else 1)
real_col += 1
if col >= column:
return real_col
return real_col

class FilePosition(object):
def __init__(self, line, column):
self.line = line
self.column = column

def point(self, view):
return view.text_point(self.line - 1, self.column - 1)
# Note, that sublime suppose that '\t' is 'tab_size' length
# But '\t' is one character
return view.text_point(self.line - 1, type_column_to_sublime_column(view, self.line, self.column))

def position_by_point(view, point):
tabs = tabs_offset(view, point)
(r, c) = view.rowcol(point)
return FilePosition(r + 1, c + 1)
return FilePosition(r + 1, c + 1 + tabs)

class RegionType(object):
def __init__(self, typename, start, end = None):
Expand Down Expand Up @@ -88,6 +115,7 @@ def parse_type_output(s):

def haskell_type(filename, module_name, line, column, cabal = None):
result = None

if get_setting_async('enable_hdevtools'):
result = hdevtools_type(filename, line, column, cabal = cabal)
if not result:
Expand All @@ -108,6 +136,8 @@ def get_types(self, filename = None, line = None, column = None):
line = r + 1
column = c + 1

column = sublime_column_to_type_column(self.view, r, c)

module_name = None
with autocompletion.database.files as files:
if filename in files:
Expand Down
10 changes: 8 additions & 2 deletions hdevtools.py
Expand Up @@ -27,15 +27,21 @@ def call_hdevtools_and_wait(arg_list, filename = None, cabal = None):
if package_db:
ghs_opts.append('-package_db {0}'.format(package_db))

ghc_opts_args = ["-g", ' '.join(ghc_opts)] if ghc_opts else []
source_dir = get_source_dir(filename)
ghc_opts.append('-i {0}'.format(source_dir))

ghc_opts_args = []
if ghc_opts:
for opt in ghc_opts:
ghc_opts_args.extend(["-g", opt])

if hdevtools_socket:
arg_list.append('--socket={0}'.format(hdevtools_socket))

try:
command = ['hdevtools'] + arg_list + ghc_opts_args

exit_code, out, err = call_and_wait(['hdevtools'] + arg_list + ghc_opts_args)
exit_code, out, err = call_and_wait(['hdevtools'] + arg_list + ghc_opts_args, cwd = source_dir)

if exit_code != 0:
raise Exception("hdevtools exited with status %d and stderr: %s" % (exit_code, err))
Expand Down
43 changes: 41 additions & 2 deletions sublime_haskell_common.py
@@ -1,6 +1,7 @@
import errno
import fnmatch
import os
import json
import sublime
import sublime_plugin
import subprocess
Expand All @@ -17,6 +18,8 @@
# Panel for SublimeHaskell errors
SUBLIME_ERROR_PANEL_NAME = 'haskell_sublime_load'

# Used to detect hs-source-dirs for project
CABAL_INSPECTOR_EXE_PATH = None

# Setting can't be get from not main threads
# So we using a trick:
Expand Down Expand Up @@ -317,6 +320,37 @@ def set_setting(key, value):
def set_setting_async(key, value):
sublime.set_timeout(lambda: set_setting(key, value), 0)

def get_source_dir(filename):
"""
Get root of hs-source-dirs for filename in project
"""
if not filename:
return os.getcwd()

(cabal_dir, project_name) = get_cabal_project_dir_and_name_of_file(filename)
if not cabal_dir:
return os.path.dirname(filename)

cabal_file = get_cabal_in_dir(cabal_dir)
exit_code, out, err = call_and_wait([CABAL_INSPECTOR_EXE_PATH, cabal_file])

if exit_code == 0:
info = json.loads(out)

dirs = ["."]

if 'error' not in info:
dirs.extend(info['source-dirs'])

paths = [os.path.normpath(os.path.join(cabal_dir, d)) for d in dirs]
paths.sort(key = lambda p: -len(p))

for p in paths:
if filename.startswith(p):
return p

return os.path.dirname(filename)

def get_cwd(filename = None):
"""
Get cwd for filename: cabal project path, file path or os.getcwd()
Expand All @@ -331,14 +365,17 @@ def call_ghcmod_and_wait(arg_list, filename=None, cabal = None):
"""

ghc_opts = get_setting_async('ghc_opts')
ghc_opts_args = ["-g", ' '.join(ghc_opts)] if ghc_opts else []
ghc_opts_args = []
if ghc_opts:
for opt in ghc_opts:
ghc_opts_args.extend(["-g", opt])

try:
command = attach_cabal_sandbox(['ghc-mod'] + arg_list + ghc_opts_args, cabal)

# log('running ghc-mod: {0}'.format(command))

exit_code, out, err = call_and_wait(command, cwd=get_cwd(filename))
exit_code, out, err = call_and_wait(command, cwd=get_source_dir(filename))

if exit_code != 0:
raise Exception("ghc-mod exited with status %d and stderr: %s" % (exit_code, err))
Expand Down Expand Up @@ -563,7 +600,9 @@ def sublime_haskell_package_path():

def plugin_loaded():
global PACKAGE_PATH
global CABAL_INSPECTOR_EXE_PATH
PACKAGE_PATH = sublime_haskell_package_path()
CABAL_INSPECTOR_EXE_PATH = os.path.join(PACKAGE_PATH, 'CabalInspector')
preload_settings()

if int(sublime.version()) < 3000:
Expand Down

0 comments on commit 7fb3078

Please sign in to comment.