Skip to content

Commit 37f7dff

Browse files
committed
feat: newfile for compile_database
1 parent ac4d9c8 commit 37f7dff

File tree

2 files changed

+70
-10
lines changed

2 files changed

+70
-10
lines changed

compile_database.py

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections import defaultdict
12
import logging
23
import os
34
import re
@@ -117,7 +118,7 @@ def filterFlags(items, fileCache):
117118
# continue
118119
if arg in { # will make sourcekit report errors
119120
"-use-frontend-parseable-output",
120-
"-emit-localized-strings"
121+
"-emit-localized-strings",
121122
# "-frontend", "-c", "-pch-disable-validation", "-index-system-modules", "-enable-objc-interop",
122123
# '-whole-module-optimization',
123124
}:
@@ -164,36 +165,89 @@ def findSwiftModuleRoot(filename):
164165

165166
return (directory, flagFile, compileFile)
166167

168+
167169
class CompileFileInfo:
168170
def __init__(self, compileFile, store):
169-
self.info = {}
171+
self.file_info = {} # {file: command}
172+
self.dir_info = None # {dir: set[file key]}
173+
self.cmd_info = None # {cmd: set[file key]}
170174

171175
# load compileFile into info
172176
import json
177+
173178
with open(compileFile) as f:
174179
m: List[dict] = json.load(f)
175180
for i in m:
176181
command = i.get("command")
177182
if not command:
178183
continue
179184
if files := i.get("files"): # batch files, eg: swift module
180-
self.info.update((self.key(f), command) for f in files)
181-
if fileLists := i.get("fileLists"): # file list store in a dedicated file
182-
self.info.update(
185+
self.file_info.update((self.key(f), command) for f in files)
186+
if fileLists := i.get(
187+
"fileLists"
188+
): # file list store in a dedicated file
189+
self.file_info.update(
183190
(self.key(f), command)
184-
for l in fileLists if os.path.isfile(l)
191+
for l in fileLists
192+
if os.path.isfile(l)
185193
for f in getFileArgs(l, store.setdefault("filelist", {}))
186194
)
187195
if file := i.get("file"): # single file info
188-
self.info[self.key(file)] = command
196+
self.file_info[self.key(file)] = command
189197

190198
def get(self, filename):
191-
if command := self.info.get(filename.lower()):
199+
if command := self.file_info.get(filename.lower()):
192200
return command.replace("\\=", "=")
193201

194202
def key(self, filename):
195203
return os.path.realpath(filename).lower()
196204

205+
def groupby_dir(self) -> dict[str, set[str]]:
206+
if self.dir_info is None: # lazy index dir and cmd
207+
self.dir_info = defaultdict(set)
208+
self.cmd_info = defaultdict(set)
209+
for f, cmd in self.file_info.items():
210+
self.dir_info[os.path.dirname(f)].add(f)
211+
self.cmd_info[cmd].add(f)
212+
213+
return self.dir_info
214+
215+
# hack new file into current compile file
216+
def new_file(self, filename):
217+
# Currently only processing swift files
218+
if not filename.endswith(".swift"):
219+
return
220+
221+
filename = os.path.realpath(filename)
222+
filename_key = filename.lower()
223+
if filename_key in self.file_info:
224+
return # already handled
225+
226+
dir = os.path.dirname(filename_key)
227+
samefile = next(
228+
(v for v in self.groupby_dir().get(dir, ()) if v.endswith(".swift")), None
229+
)
230+
if not samefile:
231+
return
232+
233+
command = self.file_info[samefile]
234+
cmd_match = next(cmd_split_pattern.finditer(command), None)
235+
if not cmd_match:
236+
return
237+
assert self.cmd_info
238+
module_files = self.cmd_info.pop(command)
239+
index = cmd_match.end()
240+
from shlex import quote
241+
242+
command = "".join((command[:index], " ", quote(filename), command[index:]))
243+
244+
# update command info
245+
self.groupby_dir()[dir].add(filename_key)
246+
module_files.add(filename_key)
247+
self.cmd_info[command] = module_files
248+
for v in module_files:
249+
self.file_info[v] = command
250+
197251

198252
def commandForFile(filename, compileFile, store: Dict):
199253
"""
@@ -202,9 +256,14 @@ def commandForFile(filename, compileFile, store: Dict):
202256
compile_store = store.setdefault("compile", {})
203257
info: CompileFileInfo = compile_store.get(compileFile)
204258
if info is None: # load {filename.lower: command} dict
205-
info = CompileFileInfo(compileFile, store) # cache first to avoid re enter when error
259+
# cache first to avoid re enter when error
260+
info = CompileFileInfo(compileFile, store)
206261
compile_store[compileFile] = info
207262

263+
# if has additional new_file, generate command for it
264+
for file in store.get("additional_files") or ():
265+
info.new_file(file)
266+
208267
# xcode 12 escape =, but not recognized...
209268
return info.get(filename)
210269

@@ -233,6 +292,7 @@ def GetFlags(filename: str, compileFile=None, **kwargs):
233292
return InferFlagsForSwift(filename, compileFile, store)
234293
return {"flags": [], "do_cache": False}
235294

295+
236296
# TODO: c family infer flags #
237297
def InferFlagsForSwift(filename, compileFile, store):
238298
"""try infer flags by convention and workspace files"""

server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def reinit_compile_info(self):
7575
"""all the compile information may change in background"""
7676

7777
# store use to save compile_datainfo. it will be reload when config changes.
78-
self.store = {}
78+
self.store = {} # main-thread
7979
self._compile_file = self.get_compile_file(self.config)
8080
if os.path.exists(self._compile_file):
8181
self.compile_file = self._compile_file

0 commit comments

Comments
 (0)