Skip to content

Commit b287b8a

Browse files
committed
[ClientServer] Work on CompleteWord
1 parent c5ebf01 commit b287b8a

File tree

12 files changed

+235
-225
lines changed

12 files changed

+235
-225
lines changed

Nitra.Visualizer.Old/MainWindow.xaml.cs

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,35 +1315,8 @@ private void ShowCompletionWindow(int pos)
13151315

13161316
private List<CompletionData> CompleteWord(int pos, IAst astRoot)
13171317
{
1318-
NSpan replacementSpan;
1319-
var parseResult = astRoot.Location.Source.File.ParseResult;
1320-
var result = NitraUtils.CompleteWord(pos, parseResult, astRoot, out replacementSpan);
13211318
var completionList = new List<CompletionData>();
1322-
1323-
foreach (var elem in result)
1324-
{
1325-
var symbol = elem as DeclarationSymbol;
1326-
if (symbol != null && symbol.IsNameValid)
1327-
{
1328-
var content = symbol.ToString();
1329-
var description = content;
1330-
// TODO: починить отображение неоднозначностей
1331-
//var amb = symbol as IAmbiguousSymbol;
1332-
//if (amb != null)
1333-
// description = Utils.WrapToXaml(string.Join(@"<LineBreak/>", amb.Ambiguous.Select(a => a.ToXaml())));
1334-
if (symbol.Name.All(IsIdenrChar))
1335-
completionList.Add(new CompletionData(replacementSpan, symbol.Name, content, description, priority: 1.0));
1336-
}
1337-
1338-
var literal = elem as string;
1339-
if (literal != null)
1340-
{
1341-
var escaped = Utils.Escape(literal);
1342-
var xaml = "<Span Foreground='blue'>" + escaped + "</Span>";
1343-
completionList.Add(new CompletionData(replacementSpan, literal, xaml, "keyword " + xaml, priority: 2.0));
1344-
}
1345-
}
1346-
1319+
// not supported
13471320
return completionList;
13481321
}
13491322

Nitra.Visualizer/CompletionData.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,7 @@ public System.Windows.Media.ImageSource Image
3333

3434
public void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs)
3535
{
36-
var line = textArea.Document.GetLineByOffset(Span.EndPos);
37-
var lineText = textArea.Document.GetText(line);
38-
var end = Span.EndPos - line.Offset;
39-
for (; end < lineText.Length; end++)
40-
{
41-
var ch = lineText[end];
42-
if (!char.IsLetterOrDigit(ch))
43-
break;
44-
}
45-
textArea.Document.Replace(Span.StartPos, end + line.Offset - Span.StartPos, this.Text);
36+
textArea.Document.Replace(Span.StartPos, Span.Length, this.Text);
4637
}
4738

4839
public int CompareTo(CompletionData other)

Nitra.Visualizer/MainWindow.xaml.cs

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,9 @@ void Response(AsyncServerMessage msg)
10621062
case AsyncServerMessage.Exception exception:
10631063
MessageBox.Show(this, "Exception occurred on the server: " + exception.exception);
10641064
break;
1065+
case AsyncServerMessage.CompleteWord completeWord:
1066+
CompleteWord(completeWord);
1067+
break;
10651068
}
10661069

10671070
if (ViewModel.CurrentFile == null || msg.FileId >= 0 && msg.FileId != ViewModel.CurrentFile.Id || msg.Version >= 0 && msg.Version != ViewModel.CurrentFile.Version)
@@ -1097,6 +1100,51 @@ void Response(AsyncServerMessage msg)
10971100
}
10981101
}
10991102

1103+
private void CompleteWord(AsyncServerMessage.CompleteWord result)
1104+
{
1105+
var replacementSpan = result.replacementSpan;
1106+
1107+
_completionWindow = new CompletionWindow(_textEditor.TextArea);
1108+
IList<ICompletionData> data = _completionWindow.CompletionList.CompletionData;
1109+
1110+
CompletionElem.Literal lit;
1111+
CompletionElem.Symbol s;
1112+
1113+
Func<CompletionElem, string> completionKeySelector = el => {
1114+
if ((lit = el as CompletionElem.Literal) != null) return lit.text;
1115+
if ((s = el as CompletionElem.Symbol) != null) return s.name;
1116+
return "";
1117+
};
1118+
1119+
var completionList = result.completionList
1120+
.Where(c => {
1121+
var key = completionKeySelector(c);
1122+
return !key.StartsWith("?") &&
1123+
!key.StartsWith("<");
1124+
})
1125+
.Distinct(completionKeySelector);
1126+
1127+
foreach (var completionData in completionList)
1128+
{
1129+
if ((lit = completionData as CompletionElem.Literal) != null)
1130+
{
1131+
var escaped = Utils.Escape(lit.text);
1132+
var xaml = "<Span Foreground='blue'>" + escaped + "</Span>";
1133+
data.Add(new CompletionData(replacementSpan, lit.text, xaml, "keyword " + xaml, priority: 1.0));
1134+
}
1135+
else if ((s = completionData as CompletionElem.Symbol) != null)
1136+
data.Add(new CompletionData(replacementSpan, s.name, s.content, s.description, priority: 1.0));
1137+
}
1138+
1139+
_completionWindow.Show();
1140+
_completionWindow.Closed += delegate
1141+
{
1142+
_completionWindow = null;
1143+
var client = ViewModel.CurrentSuite.Client;
1144+
client.Send(new ClientMessage.CompleteWordDismiss(ViewModel.CurrentProject.Id, result.FileId, result.Version));
1145+
};
1146+
}
1147+
11001148
void _fillAstTimer_Elapsed(object sender, ElapsedEventArgs e)
11011149
{
11021150
Dispatcher.Invoke(new Action(FillAst));
@@ -1356,43 +1404,6 @@ private void ShowCompletionWindow(int pos)
13561404
var currentFile = ViewModel.CurrentFile;
13571405

13581406
client.Send(new ClientMessage.CompleteWord(ViewModel.CurrentProject.Id, currentFile.Id, currentFile.Version, pos));
1359-
var result = client.Receive<ServerMessage.CompleteWord>();
1360-
var replacementSpan = result.replacementSpan;
1361-
1362-
_completionWindow = new CompletionWindow(_textEditor.TextArea);
1363-
IList<ICompletionData> data = _completionWindow.CompletionList.CompletionData;
1364-
1365-
CompletionElem.Literal lit;
1366-
CompletionElem.Symbol s;
1367-
1368-
Func<CompletionElem, string> completionKeySelector = el => {
1369-
if ((lit = el as CompletionElem.Literal) != null) return lit.text;
1370-
if ((s = el as CompletionElem.Symbol) != null) return s.name;
1371-
return "";
1372-
};
1373-
1374-
var completionList = result.completionList
1375-
.Where(c => {
1376-
var key = completionKeySelector(c);
1377-
return !key.StartsWith("?") &&
1378-
!key.StartsWith("<");
1379-
})
1380-
.Distinct(completionKeySelector);
1381-
1382-
foreach (var completionData in completionList)
1383-
{
1384-
if ((lit = completionData as CompletionElem.Literal) != null)
1385-
{
1386-
var escaped = Utils.Escape(lit.text);
1387-
var xaml = "<Span Foreground='blue'>" + escaped + "</Span>";
1388-
data.Add(new CompletionData(replacementSpan, lit.text, xaml, "keyword " + xaml, priority: 1.0));
1389-
}
1390-
else if ((s = completionData as CompletionElem.Symbol) != null)
1391-
data.Add(new CompletionData(replacementSpan, s.name, s.content, s.description, priority: 1.0));
1392-
}
1393-
1394-
_completionWindow.Show();
1395-
_completionWindow.Closed += delegate { _completionWindow = null; };
13961407
}
13971408

13981409
private void TryMatchBraces()

Nitra/ClientServer/Nitra.ClientServer.Messages/Messages.n

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ namespace Nitra.ClientServer.Messages
5757
| FileChangedBatch { id : FileId; version : FileVersion; changes : ImmutableArray[FileChange]; }
5858
| PrettyPrint { state : PrettyPrintState; }
5959
| CompleteWord { projectId : ProjectId; id : FileId; version : FileVersion; pos : int; }
60+
| CompleteWordDismiss { projectId : ProjectId; id : FileId; version : FileVersion; }
6061
| FindSymbolReferences { projectId : ProjectId; fileId : FileId; fileVersion : FileVersion; pos : int; }
6162
| FindSymbolDefinitions { projectId : ProjectId; fileId : FileId; fileVersion : FileVersion; pos : int; }
6263
| ParseTreeReflection { enable : bool; }
@@ -79,7 +80,6 @@ namespace Nitra.ClientServer.Messages
7980
[NitraMessage]
8081
public variant ServerMessage
8182
{
82-
| CompleteWord { solutionId : SolutionId; replacementSpan : NSpan; completionList : ImmutableArray[CompletionElem]; }
8383
| FindSymbolDefinitions { solutionId : SolutionId; referenceSpan : NSpan; definitions : ImmutableArray[SymbolLocation]; }
8484
| FindSymbolReferences { solutionId : SolutionId; referenceSpan : NSpan; symbols : ImmutableArray[SymbolRreferences]; }
8585
| ParseTreeReflection { solutionId : SolutionId; root : ImmutableArray[ParseTreeReflectionStruct]; }
@@ -118,7 +118,7 @@ namespace Nitra.ClientServer.Messages
118118
| Hint { text : string; referenceSpan : NSpan; }
119119
| Exception { exception : string; }
120120
| FoundDeclarations { projectId : ProjectId; declarations : ImmutableArray[DeclarationInfo]; }
121-
//| ObjectDot { objectId : int; dot : string; }
121+
| CompleteWord { replacementSpan : NSpan; completionList : ImmutableArray[CompletionElem]; }
122122

123123
public override ToString() : string { ToStringImpl() }
124124
}
@@ -449,6 +449,15 @@ namespace Nitra.ClientServer.Messages
449449
{
450450
| Literal { text : string; }
451451
| Symbol { Id : int; name : string; content : string; description : string; iconId : int; }
452+
453+
public override ToString() : string
454+
{
455+
match (this)
456+
{
457+
| Literal as x => "Literal: " + x.text
458+
| Symbol as x => "Symbol: " + x.description
459+
}
460+
}
452461
}
453462

454463
public enum ReflectionKind
@@ -809,6 +818,7 @@ namespace Nitra.ClientServer.Messages
809818
public struct FileVersion
810819
{
811820
public static Invalid : FileVersion = FileVersion(-1);
821+
public static CompleteWordMask : int = 0x1000000;
812822

813823
public Value : int;
814824

@@ -819,6 +829,9 @@ namespace Nitra.ClientServer.Messages
819829
public static @>=(x : FileVersion, y : FileVersion) : bool { x.Value >= y.Value }
820830
public static @<=(x : FileVersion, y : FileVersion) : bool { x.Value <= y.Value }
821831

832+
public static IsForCompleteWord(value : int) : bool { value %&& CompleteWordMask }
833+
public static IsForCompleteWord(this fileVersion : FileVersion) : bool { fileVersion.Value %&& CompleteWordMask }
834+
822835
public override ToString() : string { Value.ToString() }
823836
}
824837
}

Nitra/ClientServer/Nitra.ClientServer.Server/Messages.n

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ namespace Nitra.ClientServer.Server
3131
| CreateSymbolsHighlighting { solutionId : SolutionId; projectId : ProjectId; fileId : FileId; fileVersion : FileVersion; ast : IAst; }
3232
| SemanticAnalysisStart
3333
{
34-
solutionId : SolutionId;
35-
projectId : ProjectId;
36-
cancellationToken : CancellationToken;
37-
projectSupport : IProjectSupport;
38-
files : ImmutableArray[FileEvalPropertiesData];
39-
data : object;
34+
solutionId : SolutionId;
35+
projectId : ProjectId;
36+
cancellationToken : CancellationToken;
37+
projectSupport : IProjectSupport;
38+
files : ImmutableArray[FileEvalPropertiesData];
39+
data : object;
40+
completeWordPosOpt : int;
4041
}
4142
| CreatePrettyPrint { solutionId : SolutionId; projectId : ProjectId; fileId : FileId; version : FileVersion; state : PrettyPrintState; parseTree : ParseTree; }
4243
| SemanticAction { solutionId : SolutionId; action : Action[CancellationToken, SolutionId, ImmutableArray[IAst * bool]]; cancellationToken : CancellationToken; asts : ImmutableArray[IAst * bool]; }

Nitra/ClientServer/Nitra.ClientServer.Server/ParseWorker.n

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ namespace Nitra.ClientServer.Server
101101
def parseResult = parseSession.Parse(source);
102102
_mainQueue.Add(RouterAnswerMessage.FileParsed(projectId, fileId, parseResult));
103103

104-
ServerUtils.SendCompilerMessages(parseResult, _router, solutionId, projectId, fileId);
104+
unless (FileVersion.IsForCompleteWord(source.File.Version))
105+
ServerUtils.SendCompilerMessages(parseResult, _router, solutionId, projectId, fileId);
105106
ServerUtils.LogMsgHandled("ParseFile " + fileId + " Length=" + parseResult.SourceSnapshot.Text.Length, timer, 200);
106107

107108
| CreateAst(solutionId, projectId, fileId, parseResult, isActive) =>
@@ -160,16 +161,27 @@ namespace Nitra.ClientServer.Server
160161
_router.SendAsyncResponse(AsyncServerMessage.ReflectionStructCreated(fileId, version, solutionId, convertedRoot));
161162
ServerUtils.LogMsgHandled("ParseTreeReflection " + fileId, timer, 100);
162163

163-
| SemanticAnalysisStart(solutionId, projectId, cancellationToken, projectSupport, files, data) =>
164-
ServerUtils.Log($"SemanticAnalysisStart: ..$(files.Select(_.Title))");
164+
| SemanticAnalysisStart(solutionId, projectId, cancellationToken, projectSupport, files, data, completeWordPosOpt) =>
165+
ServerUtils.Log($<#SemanticAnalysisStart: $(if (completeWordPosOpt < 0) "CompletePos" + completeWordPosOpt else "") ..$(files.Select(_.Title))#>);
165166
def timer = Stopwatch.StartNew();
166167
try
167168
projectSupport.RefreshProject(cancellationToken, files, data);
168169
catch
169170
{
170171
| e is OperationCanceledException => ServerUtils.LogCanceled(e); throw;
171-
| e => SendAsyncResponse(AsyncServerMessage.RefreshProjectFailed(FileId.Invalid, FileVersion.Invalid, solutionId, e.ToString()));
172+
| e when completeWordPosOpt < 0 => SendAsyncResponse(AsyncServerMessage.RefreshProjectFailed(FileId.Invalid, FileVersion.Invalid, solutionId, e.ToString()));
172173
}
174+
175+
when (completeWordPosOpt >= 0)
176+
{
177+
// word complate
178+
foreach (file when FileVersion.IsForCompleteWord(file.FileVersion) in files)
179+
{
180+
CompleteWord(solutionId, completeWordPosOpt, file);
181+
return;
182+
}
183+
}
184+
173185
_mainQueue.Add(RouterAnswerMessage.SemanticAnalysisFinished(projectId, cancellationToken, files));
174186
def empty = array(0);
175187
for (mutable i = 0; i < files.Length; i++)
@@ -261,6 +273,78 @@ namespace Nitra.ClientServer.Server
261273
ServerUtils.LogMsgHandled("CreateMatchBrackets fileId=" + fileId, timer, 50);
262274
}
263275

276+
public CompleteWord(solutionId : SolutionId, pos : int, fileData : FileEvalPropertiesData) : void
277+
{
278+
def astRoot = fileData.Ast;
279+
def source = astRoot.Location.Source;
280+
281+
// находим цепочку ветвей AST ведущую к позиции комплита
282+
283+
def visitor = R.FindNodeAstVisitor(NSpan(pos, pos));
284+
astRoot.Accept(visitor);
285+
286+
def isEndOfIdentifier(ast : IAst) : bool
287+
{
288+
| Reference as r => r.Span.IntersectsWith(pos)
289+
| _ => false
290+
}
291+
292+
def reference = visitor.Stack.FirstOrDefault(isEndOfIdentifier);
293+
294+
when (reference == null)
295+
return;
296+
297+
// Вычисляем префикс комплита (если есть). Он будет использоваться для фильтрации списка и будет заменен выбранным словом
298+
def span = Nitra.NSpan(reference.Span.StartPos, pos);
299+
def prefix = source.Text.Substring(span.StartPos, span.Length);
300+
def replacementSpan = Nitra.NSpan(reference.Span.StartPos, reference.Span.EndPos - 1); // -1 – for fake identifier character ('\uFFFE')
301+
302+
// Находим первую ветку AST в которой есть зависимое свойство Scope и вычисляем по этому Scope список автодополнения
303+
foreach (ast in visitor.Stack)
304+
{
305+
// | BindableAst(IsScopeEvaluated = true, Scope = scope)
306+
| Reference(IsScopeEvaluated=true, IsRefEvaluated=true, Scope=scope, Ref=r) when ast.Span.IntersectsWith(span) =>
307+
//completionList.AddRange(scope.Bind(prefix));
308+
mutable r2 = r : IRef;
309+
while (r2.ResolvedTo != null)
310+
r2 = r2.ResolvedTo;
311+
312+
def symbolTypeOpt = if (r2 == null) null else r2.GetType().GetGenericArguments()[0];
313+
314+
def matcher(symbol) : bool
315+
{
316+
def symbolTypeOpt = symbolTypeOpt;
317+
def prefix = prefix;
318+
def isNameMatch = !symbol.IsNameValid || symbol.Name.StartsWith(prefix); // TODO: заменить на спецфункцию
319+
when (!isNameMatch)
320+
return false;
321+
def isTypeMatch = symbolTypeOpt == null || symbolTypeOpt.IsInstanceOfType(symbol);
322+
isTypeMatch
323+
}
324+
def br = scope.Bind(matcher);
325+
def symbols = br.Symbols;
326+
def builder = ImmutableArray.CreateBuilder(symbols.Length);
327+
328+
foreach (symbol : DeclarationSymbol in br.Symbols)
329+
{
330+
def name = symbol.Name;
331+
when (StringPatternMatching.MatchPattern(name, prefix))
332+
{
333+
def desc = symbol.ToString();
334+
def elem = CompletionElem.Symbol(symbol.Id, name, desc, desc, -1);
335+
builder.Add(elem);
336+
}
337+
}
338+
339+
def ver = FileVersion(fileData.FileVersion ^ FileVersion.CompleteWordMask);
340+
def msg = AsyncServerMessage.CompleteWord(FileId(fileData.FileId), ver, solutionId, ServerUtils.ConvertNSpan(replacementSpan), builder.MoveToImmutable());
341+
SendAsyncResponse(msg);
342+
break;
343+
344+
| _ => ()
345+
}
346+
}
347+
264348
ConvertParseTreeReflection(node : R.ReflectionStruct, objectMap : Hashtable[R.ReflectionStruct, ParseTreeReflectionStruct]) : ParseTreeReflectionStruct
265349
{
266350
mutable result;

0 commit comments

Comments
 (0)