Skip to content

Commit

Permalink
Do error checking incrementally
Browse files Browse the repository at this point in the history
Together with the previous commit, this fixes the UI responsiveness
during error checking in larger projects. Previously, the UI could hang
until error checking completed.
  • Loading branch information
jeffrey-easyesi committed Nov 19, 2015
1 parent e6814f4 commit 7f6051a
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 45 deletions.
2 changes: 1 addition & 1 deletion nbproject/genfiles.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ build.xml.script.CRC32=c7dc402d
build.xml.stylesheet.CRC32=[email protected]
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=80c0b2c2
nbproject/build-impl.xml.data.CRC32=d826c7a8
nbproject/build-impl.xml.script.CRC32=8b04660f
nbproject/build-impl.xml.stylesheet.CRC32=[email protected]
9 changes: 9 additions & 0 deletions nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
<specification-version>1.24.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.api.progress</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>1</release-version>
<specification-version>1.38.1</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.core.multiview</code-name-base>
<build-prerequisite/>
Expand Down
3 changes: 2 additions & 1 deletion src/netbeanstypescript/TSIndexerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import org.openide.NotifyDescriptor;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.RequestProcessor;

/**
* This "indexer" doesn't really index anything, it's just a way to read all the TS files in a
Expand Down Expand Up @@ -97,7 +98,7 @@ protected void index(Iterable<? extends Indexable> files, Context context) {

@Override
public void scanFinished(Context context) {
TSService.scanFinished(context);
TSService.updateErrors(context.getRootURI());
}

@Override
Expand Down
104 changes: 68 additions & 36 deletions src/netbeanstypescript/TSService.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
Expand All @@ -58,6 +57,8 @@
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.modules.csl.api.Severity;
import org.netbeans.modules.csl.spi.DefaultError;
import org.netbeans.modules.parsing.api.Snapshot;
Expand All @@ -69,6 +70,7 @@
import org.openide.filesystems.FileObject;
import org.openide.filesystems.URLMapper;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.RequestProcessor;

/**
*
Expand All @@ -77,6 +79,7 @@
public class TSService {

static final Logger log = Logger.getLogger(TSService.class.getName());
static final RequestProcessor RP = new RequestProcessor("TSService", 1, true);

private static class ExceptionFromJS extends Exception {
ExceptionFromJS(String msg) { super(msg); }
Expand Down Expand Up @@ -196,6 +199,7 @@ private static class ProgramData {
final Map<String, FileObject> files = new HashMap<>();
final Map<String, Indexable> indexables = new HashMap<>();
boolean needErrorsUpdate;
Object currentErrorsUpdate;

ProgramData() throws Exception {
progVar = "p" + nodejs.nextProgId++;
Expand All @@ -222,11 +226,11 @@ Object call(String method, Object... args) {
}

final void setFileSnapshot(String relPath, Indexable indexable, Snapshot s, boolean modified) {
needErrorsUpdate = true;
call("updateFile", relPath, s.getText(), modified);
files.put(relPath, s.getSource().getFileObject());
if (indexable != null) {
indexables.put(relPath, indexable);
needErrorsUpdate = true;
}
}

Expand Down Expand Up @@ -298,49 +302,76 @@ static void removeFile(Indexable indxbl, Context cntxt) {
}
}

static void scanFinished(Context context) {
static final Convertor<JSONObject> errorConvertor = new Convertor<JSONObject>() {
@Override
public ErrorsCache.ErrorKind getKind(JSONObject err) {
int category = ((Number) err.get("category")).intValue();
return category == 0 ? ErrorsCache.ErrorKind.WARNING
: ErrorsCache.ErrorKind.ERROR;
}
@Override
public int getLineNumber(JSONObject err) {
return ((Number) err.get("line")).intValue();
}
@Override
public String getMessage(JSONObject err) {
return (String) err.get("messageText");
}
};

static void updateErrors(final URL rootURI) {
final ProgramData program;
final Object currentUpdate;
final Indexable[] files;
lock.lock();
try {
ProgramData program = programs.get(context.getRootURI());
program = programs.get(rootURI);
if (program == null || ! program.needErrorsUpdate) {
return;
}
program.needErrorsUpdate = false;
// TODO: this is slow and locks out other TSService usage for too long in big projects
JSONObject diags = (JSONObject) program.call("getAllDiagnostics");
if (diags == null) {
return;
}
for (String fileName: (Set<String>) diags.keySet()) {
JSONArray errors = (JSONArray) diags.get(fileName);
Indexable i = program.indexables.get(fileName);
if (i == null) {
log.log(Level.WARNING, "{0}: No indexable!", fileName);
continue;
}
ErrorsCache.setErrors(context.getRootURI(), i, errors, new Convertor<JSONObject>() {
@Override
public ErrorsCache.ErrorKind getKind(JSONObject err) {
int category = ((Number) err.get("category")).intValue();
if (category == 0) {
return ErrorsCache.ErrorKind.WARNING;
} else {
return ErrorsCache.ErrorKind.ERROR;
}
}
@Override
public int getLineNumber(JSONObject err) {
return ((Number) err.get("line")).intValue();
}
@Override
public String getMessage(JSONObject err) {
return (String) err.get("messageText");
}
});
}
program.currentErrorsUpdate = currentUpdate = new Object();
files = program.indexables.values().toArray(new Indexable[0]);
} finally {
lock.unlock();
}
new Runnable() {
RequestProcessor.Task task = RP.create(this);
ProgressHandle progress = ProgressHandleFactory.createHandle("TypeScript error checking", task);
@Override
public void run() {
progress.start(files.length);
try {
long t1 = System.currentTimeMillis();
for (int i = 0; i < files.length; i++) {
Indexable indexable = files[i];
String fileName = indexable.getRelativePath();
progress.progress(fileName, i);
if (fileName.endsWith(".json")) {
continue;
}
lock.lockInterruptibly();
try {
if (program.currentErrorsUpdate != currentUpdate) {
return; // this task has been superseded
}
JSONArray errors = (JSONArray) program.call("getDiagnostics", fileName);
if (errors != null) {
ErrorsCache.setErrors(rootURI, indexable, errors, errorConvertor);
}
} finally {
lock.unlock();
}
}
log.log(Level.FINE, "updateErrors for {0} completed in {1}ms",
new Object[] { rootURI, System.currentTimeMillis() - t1 });
} catch (InterruptedException e) {
log.log(Level.INFO, "updateErrors for {0} cancelled by user", rootURI);
} finally {
progress.finish();
}
}
}.task.schedule(0);
}

static void removeProgram(URL rootURL) {
Expand All @@ -350,6 +381,7 @@ static void removeProgram(URL rootURL) {
if (program == null) {
return;
}
program.currentErrorsUpdate = null; // stop any updateErrors task

Iterator<FileData> iter = allFiles.values().iterator();
while (iter.hasNext()) {
Expand Down
7 changes: 0 additions & 7 deletions ts/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,6 @@ class Program {
}
return errs;
}
getAllDiagnostics() {
var errs: any = {};
for (var fileName in this.host.files) {
errs[fileName] = this.getDiagnostics(fileName);
}
return errs;
}
getCompletions(fileName: string, position: number, prefix: string, isPrefixMatch: boolean, caseSensitive: boolean) {
var service = this.service;
var info = service.getCompletionsAtPosition(fileName, position);
Expand Down

0 comments on commit 7f6051a

Please sign in to comment.