3737
3838"""ECMAScript support for CodeIntel"""
3939
40- from __future__ import absolute_import
41- from __future__ import print_function
40+ from __future__ import absolute_import , print_function , division
4241
4342import os
4443from os .path import exists , dirname , join , normcase , basename
4948import re
5049from pprint import pformat
5150import json
52- import bisect
51+ from collections import namedtuple
5352
5453from SilverCity import ScintillaConstants
5554
@@ -500,7 +499,7 @@ def _buf_indep_libs_from_env(self, env):
500499 paths_from_libname [STATE ].append (dir )
501500 log .debug ("ECMAScript %s paths for each lib:\n %s" , ver , indent (pformat (paths_from_libname )))
502501
503- # - envlib, sitelib, cataloglib, stdlib
502+ # - envlib, sitelib, cataloglib, nodelib, stdlib
504503 if paths_from_libname ["envlib" ]:
505504 libs .append (db .get_lang_lib (self .lang , "envlib" , paths_from_libname ["envlib" ]))
506505 if paths_from_libname ["sitelib" ]:
@@ -716,33 +715,54 @@ def libs(self):
716715 return self .langintel .libs_from_buf (self )
717716
718717 def scoperef_from_blob_and_line (self , blob , line ): # line is 1-based
719- def _scoperef_from_blob_and_line (lpath , scope ): # line is 1-based
720- end = 0
718+ def _scoperef_from_blob_and_line (result , scope , parent_path ):
719+ start = None
720+ end = None
721721 subscopes = scope .findall ("scope" )
722- if subscopes :
723- lines = [int (s .get ("line" , 0 )) for s in subscopes ]
724- num_lines = len (lines )
725- pos = bisect .bisect (lines , line )
726- if pos < 1 :
727- return 0
728- while pos <= num_lines :
729- line_pos = lines [pos - 1 ]
730- if line_pos > line :
731- break
732- subscope = subscopes [pos - 1 ]
733- end = max (
734- end ,
735- int (scope .get ("lineend" , line_pos )),
736- _scoperef_from_blob_and_line (lpath , subscope ),
737- )
738- if line <= end :
739- lpath .insert (0 , subscope .get ("name" ))
740- break
741- pos += 1
742- return end
743- lpath = []
744- _scoperef_from_blob_and_line (lpath , blob )
745- return (blob , lpath )
722+ for subscope in subscopes :
723+ path = parent_path + [subscope ]
724+ # Get current scope line and lineend:
725+ linestart = int (subscope .get ("line" , 0 ))
726+ lineend = int (subscope .get ("lineend" , linestart ))
727+ # Get first and last line in all subecopes:
728+ _linestart , _lineend = _scoperef_from_blob_and_line (result , subscope , path )
729+ if _linestart is not None and linestart > _linestart :
730+ linestart = _linestart
731+ if _lineend is not None and lineend < _lineend :
732+ lineend = _lineend
733+ # Keep track of start and end:
734+ if start is None or start > linestart :
735+ start = linestart
736+ if end is None or end < lineend :
737+ end = lineend
738+ # If the line being requested is in range...
739+ if line >= linestart and line <= lineend :
740+ delta = line - linestart
741+ # Check if it's closer to the already found result:
742+ if delta <= 0 :
743+ # It's at the line or after...
744+ delta = - delta
745+ if result .delta is None or delta < result .delta :
746+ # it's closer, set result
747+ result .delta = delta
748+ result .path = path
749+ elif delta == result .delta :
750+ if len (path ) > len (result .path ):
751+ # it's the same but it's a deeper scope, set result
752+ result .delta = delta
753+ result .path = path
754+ else :
755+ # it's before...
756+ if result .delta is None or delta < result .delta :
757+ # it's closer, set result
758+ result .delta = delta
759+ result .path = path
760+ return start , end
761+ result = namedtuple ("Result" , "delta path" )
762+ result .delta = None
763+ result .path = []
764+ start , end = _scoperef_from_blob_and_line (result , blob , [])
765+ return (blob , [subscope .get ("name" ) for subscope in result .path ])
746766
747767 def trg_from_pos (self , pos , implicit = True ):
748768 """ECMAScript trigger types:
@@ -1129,7 +1149,7 @@ def __init__(self, mgr):
11291149 )
11301150 subpaths_re = re .compile (r'(^|/)(?:%s)($|/)' % r'|' .join (subpaths ))
11311151
1132- def _find_importable (self , imp_dir , name , find_package = True ):
1152+ def _find_importable (self , imp_dir , name , boost , find_package = True ):
11331153 mod , suffix = os .path .splitext (name )
11341154 if mod != 'index' :
11351155 suffixes = self .suffixes
@@ -1148,25 +1168,27 @@ def _find_importable(self, imp_dir, name, find_package=True):
11481168 for subpath in self .subpaths :
11491169 _name = os .path .join (name , self .subpaths_re .sub (r'\1%s\2' % subpath , main ))
11501170 if os .path .exists (os .path .join (imp_dir , dirname (_name ))):
1151- module = self ._find_importable (imp_dir , _name )
1171+ module = self ._find_importable (imp_dir , _name , boost // 2 , find_package = False )
11521172 if module :
1153- return (module [0 ], name , module [2 ])
1173+ # Remove subpath from module name
1174+ module = (module [0 ], name , module [2 ])
1175+ return module
11541176
11551177 if not suffix :
11561178 for _suffix in suffixes :
11571179 _name = dirname (name )
11581180 _mod = basename (name )
11591181 init = os .path .join (_name , _mod + _suffix )
11601182 if os .path .exists (os .path .join (imp_dir , init )):
1161- return (suffixes_dict [_suffix ], _name , (init , _mod , False ))
1183+ return (suffixes_dict [_suffix ] + boost , _name , (init , _mod , False ))
11621184
11631185 for _suffix in suffixes :
11641186 init = os .path .join (name , 'index' + _suffix )
11651187 if os .path .exists (os .path .join (imp_dir , init )):
1166- return (suffixes_dict [_suffix ], name , (init , 'index' , False ))
1188+ return (suffixes_dict [_suffix ] + boost , name , (init , 'index' , False ))
11671189
11681190 if suffix in suffixes :
1169- return (suffixes_dict [suffix ], mod , (name , basename (mod ), False ))
1191+ return (suffixes_dict [suffix ] + boost , mod , (name , basename (mod ), False ))
11701192
11711193 def find_importables_in_dir (self , imp_dir ):
11721194 """See citadel.py::ImportHandler.find_importables_in_dir() for
@@ -1189,20 +1211,37 @@ def find_importables_in_dir(self, imp_dir):
11891211 # TODO: stop these getting in here.
11901212 return {}
11911213
1192- importables = {}
1214+ modules = []
11931215
11941216 if os .path .isdir (imp_dir ):
1195- modules = []
11961217 for name in os .listdir (imp_dir ):
11971218 if not name .startswith ('.' ):
1198- module = self ._find_importable (imp_dir , name )
1219+ module = self ._find_importable (imp_dir , name , 100 )
11991220 if module :
12001221 modules .append (module )
1201- modules .sort (key = lambda mod : mod [0 ])
12021222
1203- for _ , mod , importable in modules :
1204- if mod not in importables :
1205- importables [mod ] = importable
1223+ package_json = os .path .join (imp_dir , 'package.json' )
1224+ if os .path .exists (package_json ):
1225+ for subpath in self .subpaths :
1226+ _imp_dir = os .path .join (imp_dir , subpath )
1227+ if os .path .isdir (_imp_dir ):
1228+ for name in os .listdir (_imp_dir ):
1229+ if not name .startswith ('.' ):
1230+ module = self ._find_importable (imp_dir , os .path .join (subpath , name ), 0 , find_package = False )
1231+ if module :
1232+ # Remove subpath from module name
1233+ mod , suffix = os .path .splitext (name )
1234+ module = (module [0 ], mod , module [2 ])
1235+ modules .append (module )
1236+ break
1237+
1238+ modules .sort (key = lambda mod : mod [0 ])
1239+
1240+ importables = {}
1241+
1242+ for _ , mod , importable in modules :
1243+ if mod not in importables :
1244+ importables [mod ] = importable
12061245
12071246 return importables
12081247
0 commit comments