diff --git a/docs/webkit.md b/docs/webkit.md index 6e8010f70..0719c8ac7 100644 --- a/docs/webkit.md +++ b/docs/webkit.md @@ -1,2 +1,480 @@ -* Install webkit +* Get webkit source (~30 minutes) `git clone --depth 1 https://github.com/WebKit/webkit.git` + `cd webkit` + +* Apply tmm1's patch (found at end of this document) + `patch -p1 < tmm1.patch` + +* Build webkit (~2 hours) + `Tools/Scripts/build-webkit` + + +# tmm1's patch +``` + +diff --git a/LayoutTests/fast/js/exception-properties-expected.txt b/LayoutTests/fast/js/exception-properties-expected.txt +index 36d7e84..573ef10 100644 +--- a/LayoutTests/fast/js/exception-properties-expected.txt ++++ b/LayoutTests/fast/js/exception-properties-expected.txt +@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE + + + PASS enumerableProperties(error) is [] +-PASS enumerableProperties(nativeError) is ["line", "sourceId", "sourceURL"] ++PASS enumerableProperties(nativeError) is ["line", "sourceId", "sourceURL", "stack"] + PASS Object.getPrototypeOf(nativeError).name is "RangeError" + PASS Object.getPrototypeOf(nativeError).message is "" + PASS successfullyParsed is true +diff --git a/LayoutTests/fast/js/script-tests/exception-properties.js b/LayoutTests/fast/js/script-tests/exception-properties.js +index 30789a0..f7c2871 100644 +--- a/LayoutTests/fast/js/script-tests/exception-properties.js ++++ b/LayoutTests/fast/js/script-tests/exception-properties.js +@@ -16,7 +16,7 @@ try { + var error = new Error("message"); + + shouldBe('enumerableProperties(error)', '[]'); +- shouldBe('enumerableProperties(nativeError)', '["line", "sourceId", "sourceURL"]'); ++ shouldBe('enumerableProperties(nativeError)', '["line", "sourceId", "sourceURL", "jscStack"]'); + + shouldBe('Object.getPrototypeOf(nativeError).name', '"RangeError"'); + shouldBe('Object.getPrototypeOf(nativeError).message', '""'); +diff --git a/LayoutTests/platform/chromium/test_expectations.txt b/LayoutTests/platform/chromium/test_expectations.txt +index f6e35e0..e6d0b90 100644 +--- a/LayoutTests/platform/chromium/test_expectations.txt ++++ b/LayoutTests/platform/chromium/test_expectations.txt +@@ -518,6 +518,10 @@ WONTFIX SKIP : fast/frames/cross-site-this.html = FAIL + // throw. V8 follows the spec. + WONTFIX SKIP : fast/js/reparsing-semicolon-insertion.html = FAIL + ++// This tests stack-traces that are generated by JSC. This test should ++// fail since it is specific to jsc. ++WONTFIX SKIP : fast/js/stack-trace.html = FAIL ++ + // Rubber-banding is currently a CG only feature. + WONTFIX : platform/chromium/rubberbanding = FAIL + WONTFIX : platform/chromium/compositing/rubberbanding = IMAGE +diff --git a/Source/JavaScriptCore/JavaScriptCore.exp b/Source/JavaScriptCore/JavaScriptCore.exp +index 4bf3ed9..1d11aea 100644 +--- a/Source/JavaScriptCore/JavaScriptCore.exp ++++ b/Source/JavaScriptCore/JavaScriptCore.exp +@@ -117,6 +117,7 @@ __ZN3JSC10JSFunction6s_infoE + __ZN3JSC10JSFunctionC1EPNS_9ExecStateEPNS_14JSGlobalObjectEPNS_9StructureE + __ZN3JSC10throwErrorEPNS_9ExecStateENS_7JSValueE + __ZN3JSC10throwErrorEPNS_9ExecStateEPNS_8JSObjectE ++__ZN3JSC11Interpreter13getStackTraceEPNS_12JSGlobalDataEiRN3WTF6VectorINS_10StackFrameELm0EEE + __ZN3JSC11JSByteArray10putByIndexEPNS_6JSCellEPNS_9ExecStateEjNS_7JSValueE + __ZN3JSC11JSByteArray15createStructureERNS_12JSGlobalDataEPNS_14JSGlobalObjectENS_7JSValueEPKNS_9ClassInfoE + __ZN3JSC11JSByteArray18getOwnPropertySlotEPNS_6JSCellEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE +diff --git a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def +index 5095d5c..13ee907 100644 +--- a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def ++++ b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def +@@ -208,6 +208,7 @@ EXPORTS + ?getPropertyDescriptor@JSObject@JSC@@QAE_NPAVExecState@2@ABVIdentifier@2@AAVPropertyDescriptor@2@@Z + ?getPropertyNames@JSObject@JSC@@SAXPAV12@PAVExecState@2@AAVPropertyNameArray@2@W4EnumerationMode@2@@Z + ?getSlice@ArgList@JSC@@QBEXHAAV12@@Z ++ ?getStackTrace@Interpreter@JSC@@SAXPAVJSGlobalData@2@HAAV?$Vector@UStackFrame@JSC@@$0A@@WTF@@@Z + ?getString@JSCell@JSC@@QBE?AVUString@2@PAVExecState@2@@Z + ?getString@JSCell@JSC@@QBE_NPAVExecState@2@AAVUString@2@@Z + ?getter@PropertyDescriptor@JSC@@QBE?AVJSValue@2@XZ +diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp +index 4194901..b9d07dd 100644 +--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp ++++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp +@@ -45,7 +45,6 @@ + #include "JSActivation.h" + #include "JSArray.h" + #include "JSByteArray.h" +-#include "JSFunction.h" + #include "JSNotAnObject.h" + #include "JSPropertyNameIterator.h" + #include "LiteralParser.h" +@@ -790,6 +789,95 @@ static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception, + exception->putDirect(*globalData, globalData->propertyNames->message, jsString(globalData, message)); + } + ++static void getCallerLine(JSGlobalData* globalData, CallFrame* callFrame, int& lineNumber) ++{ ++ (void)globalData; ++ unsigned bytecodeOffset; ++ lineNumber = -1; ++ callFrame = callFrame->removeHostCallFrameFlag(); ++ ++ if (callFrame->callerFrame() == CallFrame::noCaller() || callFrame->callerFrame()->hasHostCallFrameFlag()) ++ return; ++ ++ CodeBlock* callerCodeBlock = callFrame->callerFrame()->removeHostCallFrameFlag()->codeBlock(); ++ ++#if ENABLE(INTERPRETER) ++ if (!globalData->canUseJIT()) ++ bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnVPC()); ++#if ENABLE(JIT) ++ else ++ bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC()); ++#endif ++#else ++ bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC()); ++#endif ++ ++ lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1); ++} ++ ++static ALWAYS_INLINE const UString getSourceURLFromCallFrame(CallFrame* callFrame) ++{ ++ if (callFrame->hasHostCallFrameFlag()) ++ return UString(); ++#if ENABLE(INTERPRETER) ++ if (!callFrame->globalData().canUseJIT()) ++ return callFrame->codeBlock()->source()->url(); ++#if ENABLE(JIT) ++ return callFrame->codeBlock()->ownerExecutable()->sourceURL(); ++#endif ++#else ++ return callFrame->codeBlock()->ownerExecutable()->sourceURL(); ++#endif ++} ++ ++static StackFrameCodeType getStackFrameCodeType(CallFrame* callFrame) ++{ ++ if (callFrame->hasHostCallFrameFlag()) ++ return StackFrameNativeCode; ++ ++ switch (callFrame->codeBlock()->codeType()) { ++ case EvalCode: ++ return StackFrameEvalCode; ++ case FunctionCode: ++ return StackFrameFunctionCode; ++ case GlobalCode: ++ return StackFrameGlobalCode; ++ } ++ ASSERT_NOT_REACHED(); ++ return StackFrameGlobalCode; ++} ++ ++void Interpreter::getStackTrace(JSGlobalData* globalData, int line, Vector& results) ++{ ++ int stackLimit = 32; ++ CallFrame* callFrame = globalData->topCallFrame->removeHostCallFrameFlag(); ++ if (!callFrame || callFrame == CallFrame::noCaller() || !callFrame->codeBlock()) ++ return; ++ UString sourceURL; ++ UString traceLevel; ++ ++ for (int i = 0; i < stackLimit; ++i) { ++ if (!callFrame || callFrame == CallFrame::noCaller()) ++ break; ++ if (callFrame->codeBlock()) { ++ sourceURL = getSourceURLFromCallFrame(callFrame); ++ ++ StackFrame s = { ++ Strong(*globalData, callFrame->callee()), ++ Strong(*globalData, callFrame), ++ getStackFrameCodeType(callFrame), ++ Strong(*globalData, callFrame->codeBlock()->ownerExecutable()), ++ line, ++ sourceURL ++ }; ++ ++ results.append(s); ++ } ++ getCallerLine(globalData, callFrame, line); ++ callFrame = callFrame->callerFrame()->removeHostCallFrameFlag(); ++ } ++} ++ + NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset) + { + CodeBlock* codeBlock = callFrame->codeBlock(); +@@ -808,7 +896,9 @@ NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSV + + // FIXME: should only really be adding these properties to VM generated exceptions, + // but the inspector currently requires these for all thrown objects. +- addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source()); ++ Vector stackTrace; ++ getStackTrace(&callFrame->globalData(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), stackTrace); ++ addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source(), stackTrace); + } + + isInterrupt = isInterruptedExecutionException(exception) || isTerminatedExecutionException(exception); +diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h +index 6dfd331..748e8fb 100644 +--- a/Source/JavaScriptCore/interpreter/Interpreter.h ++++ b/Source/JavaScriptCore/interpreter/Interpreter.h +@@ -31,10 +31,12 @@ + + #include "ArgList.h" + #include "JSCell.h" ++#include "JSFunction.h" + #include "JSValue.h" + #include "JSObject.h" + #include "Opcode.h" + #include "RegisterFile.h" ++#include "StrongInlines.h" + + #include + +@@ -42,8 +44,8 @@ namespace JSC { + + class CodeBlock; + class EvalExecutable; ++ class ExecutableBase; + class FunctionExecutable; +- class JSFunction; + class JSGlobalObject; + class ProgramExecutable; + class Register; +@@ -62,16 +64,79 @@ namespace JSC { + WillExecuteStatement + }; + ++ enum StackFrameCodeType { ++ StackFrameGlobalCode, ++ StackFrameEvalCode, ++ StackFrameFunctionCode, ++ StackFrameNativeCode ++ }; ++ ++ struct StackFrame { ++ Strong callee; ++ Strong callFrame; ++ StackFrameCodeType codeType; ++ Strong executable; ++ int line; ++ UString sourceURL; ++ UString toString() const ++ { ++ bool hasSourceURLInfo = !sourceURL.isNull() && !sourceURL.isEmpty(); ++ bool hasLineInfo = line > -1; ++ String traceLine; ++ String sourceInfo; ++ JSObject* stackFrameCallee = callee.get(); ++ UString functionName = ""; ++ ++ if (hasSourceURLInfo) ++ sourceInfo = hasLineInfo ? String::format("%s:%d", sourceURL.ascii().data(), line) ++ : String::format("%s", sourceURL.ascii().data()); ++ ++ if (stackFrameCallee && stackFrameCallee->inherits(&JSFunction::s_info)) ++ functionName = asFunction(stackFrameCallee)->name(callFrame.get()); ++ ++ switch (codeType) { ++ case StackFrameEvalCode: ++ if (hasSourceURLInfo && !functionName.isEmpty()) ++ traceLine = String::format("at eval at %s (%s)", functionName.ascii().data(), sourceInfo.ascii().data()); ++ else if (hasSourceURLInfo) ++ traceLine = String::format("at eval at (%s)", sourceInfo.ascii().data()); ++ else if (!functionName.isEmpty()) ++ traceLine = String::format("at eval at %s", functionName.ascii().data()); ++ else ++ traceLine = String::format("at eval"); ++ break; ++ case StackFrameNativeCode: ++ if (!functionName.isEmpty()) ++ traceLine = String::format("at %s (native)", functionName.ascii().data()); ++ else ++ traceLine = "at (native)"; ++ break; ++ case StackFrameFunctionCode: ++ case StackFrameGlobalCode: ++ if (hasSourceURLInfo && !functionName.isEmpty()) ++ traceLine = String::format("at %s (%s)", functionName.ascii().data(), sourceInfo.ascii().data()); ++ else if (hasSourceURLInfo) ++ traceLine = String::format("at %s", sourceInfo.ascii().data()); ++ else if (!functionName.isEmpty()) ++ traceLine = String::format("at %s", functionName.ascii().data()); ++ else ++ traceLine = String::format("at unknown source"); ++ break; ++ } ++ return traceLine.impl(); ++ } ++ }; ++ + class TopCallFrameSetter { + public: + TopCallFrameSetter(JSGlobalData& global, CallFrame* callFrame) + : globalData(global) +- , oldCallFrame(global.topCallFrame) ++ , oldCallFrame(global.topCallFrame) + { + global.topCallFrame = callFrame; + } +- +- ~TopCallFrameSetter() ++ ++ ~TopCallFrameSetter() + { + globalData.topCallFrame = oldCallFrame; + } +@@ -141,6 +206,8 @@ namespace JSC { + + NEVER_INLINE HandlerInfo* throwException(CallFrame*&, JSValue&, unsigned bytecodeOffset); + NEVER_INLINE void debug(CallFrame*, DebugHookID, int firstLine, int lastLine); ++ static const UString getTraceLine(CallFrame*, StackFrameCodeType, const UString&, int); ++ static void getStackTrace(JSGlobalData*, int line, Vector& results); + + void dumpSampleData(ExecState* exec); + void startSampling(); +diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp +index 47ec8c6..6d82f54 100644 +--- a/Source/JavaScriptCore/jsc.cpp ++++ b/Source/JavaScriptCore/jsc.cpp +@@ -27,6 +27,7 @@ + #include "CurrentTime.h" + #include "ExceptionHelpers.h" + #include "InitializeThreading.h" ++#include "Interpreter.h" + #include "JSArray.h" + #include "JSFunction.h" + #include "JSLock.h" +@@ -78,6 +79,7 @@ static bool fillBufferWithContentsOfFile(const UString& fileName, Vector& + + static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*); + static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*); ++static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*); + static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*); + #ifndef NDEBUG + static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*); +@@ -184,6 +186,7 @@ protected: + addFunction(globalData, "run", functionRun, 1); + addFunction(globalData, "load", functionLoad, 1); + addFunction(globalData, "checkSyntax", functionCheckSyntax, 1); ++ addFunction(globalData, "jscStack", functionJSCStack, 1); + addFunction(globalData, "readline", functionReadline, 0); + addFunction(globalData, "preciseTime", functionPreciseTime, 0); + #if ENABLE(SAMPLING_FLAGS) +@@ -252,6 +255,22 @@ EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec) + return JSValue::encode(jsUndefined()); + } + ++EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec) ++{ ++ String trace = "--> Stack trace:\n"; ++ Vector stackTrace; ++ Interpreter::getStackTrace(&exec->globalData(), -1, stackTrace); ++ int i = 0; ++ ++ for (Vector::iterator iter = stackTrace.begin(); iter < stackTrace.end(); iter++) { ++ StackFrame level = *iter; ++ trace += String::format(" %i %s\n", i, level.toString().utf8().data()); ++ i++; ++ } ++ fprintf(stderr, "%s", trace.utf8().data()); ++ return JSValue::encode(jsUndefined()); ++} ++ + EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec) + { + JSLock lock(SilenceAssertionsOnly); +diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h +index f3d96ff..27dafae 100644 +--- a/Source/JavaScriptCore/parser/Parser.h ++++ b/Source/JavaScriptCore/parser/Parser.h +@@ -1016,7 +1016,7 @@ PassRefPtr Parser::parse(JSGlobalObject* lexicalGlobalObj + else if (isEvalNode()) + *exception = createSyntaxError(lexicalGlobalObject, errMsg); + else +- *exception = addErrorInfo(&lexicalGlobalObject->globalData(), createSyntaxError(lexicalGlobalObject, errMsg), errLine, *m_source); ++ *exception = addErrorInfo(&lexicalGlobalObject->globalData(), createSyntaxError(lexicalGlobalObject, errMsg), errLine, *m_source, Vector()); + } + + if (debugger && !ParsedNode::scopeIsFunction) +diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h +index 08d8644..ce11730 100644 +--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h ++++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h +@@ -52,6 +52,7 @@ + macro(input) \ + macro(isArray) \ + macro(isPrototypeOf) \ ++ macro(stack) \ + macro(length) \ + macro(message) \ + macro(multiline) \ +diff --git a/Source/JavaScriptCore/runtime/Error.cpp b/Source/JavaScriptCore/runtime/Error.cpp +index c3f2878..9b4af6f 100644 +--- a/Source/JavaScriptCore/runtime/Error.cpp ++++ b/Source/JavaScriptCore/runtime/Error.cpp +@@ -27,6 +27,7 @@ + #include "ConstructData.h" + #include "ErrorConstructor.h" + #include "FunctionPrototype.h" ++#include "JSArray.h" + #include "JSFunction.h" + #include "JSGlobalObject.h" + #include "JSObject.h" +@@ -39,6 +40,8 @@ namespace JSC { + static const char* linePropertyName = "line"; + static const char* sourceIdPropertyName = "sourceId"; + static const char* sourceURLPropertyName = "sourceURL"; ++static const char* stackPropertyName = "stack"; ++static const char* messagePropertyName = "message"; + + JSObject* createError(JSGlobalObject* globalObject, const UString& message) + { +@@ -117,7 +120,7 @@ JSObject* createURIError(ExecState* exec, const UString& message) + return createURIError(exec->lexicalGlobalObject(), message); + } + +-JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source) ++JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source, const Vector& stackTrace) + { + intptr_t sourceID = source.provider()->asID(); + const UString& sourceURL = source.provider()->url(); +@@ -128,13 +131,29 @@ JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, cons + error->putDirect(*globalData, Identifier(globalData, sourceIdPropertyName), jsNumber((double)sourceID), ReadOnly | DontDelete); + if (!sourceURL.isNull()) + error->putDirect(*globalData, Identifier(globalData, sourceURLPropertyName), jsString(globalData, sourceURL), ReadOnly | DontDelete); ++ if (!stackTrace.isEmpty()) { ++ String trace = String::format("%s: ", JSObject::className(error).ascii().data()); ++ trace += asString(error->getDirect(*globalData, Identifier(globalData, messagePropertyName)))->tryGetValue().ascii().data(); ++ trace += "\n"; ++ //JSArray* stackTraceArray = JSArray::create(*globalData, globalData->dynamicGlobalObject->arrayStructure()); ++ for (unsigned i = 0; i < stackTrace.size(); i++) { ++ UString stackLevel = stackTrace[i].toString(); ++ trace += " "; ++ trace += stackLevel.ascii().data(); ++ if (i < stackTrace.size() - 1) ++ trace += "\n"; ++ //stackTraceArray->push(globalData->topCallFrame, jsString(globalData, stackLevel)); ++ } ++ //error->putDirect(*globalData, Identifier(globalData, stackPropertyName), stackTraceArray, ReadOnly | DontDelete); ++ error->putDirect(*globalData, Identifier(globalData, stackPropertyName), jsString(globalData, UString(trace.impl())), ReadOnly | DontDelete); ++ } + + return error; + } + +-JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source) ++JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source, const Vector& stackTrace) + { +- return addErrorInfo(&exec->globalData(), error, line, source); ++ return addErrorInfo(&exec->globalData(), error, line, source, stackTrace); + } + + bool hasErrorInfo(ExecState* exec, JSObject* error) +diff --git a/Source/JavaScriptCore/runtime/Error.h b/Source/JavaScriptCore/runtime/Error.h +index 88b540a..59b3949 100644 +--- a/Source/JavaScriptCore/runtime/Error.h ++++ b/Source/JavaScriptCore/runtime/Error.h +@@ -24,6 +24,7 @@ + #define Error_h + + #include "InternalFunction.h" ++#include "Interpreter.h" + #include "JSObject.h" + #include + +@@ -56,9 +57,9 @@ namespace JSC { + + // Methods to add + bool hasErrorInfo(ExecState*, JSObject* error); +- JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&); ++ JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&, const Vector&); + // ExecState wrappers. +- JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&); ++ JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&, const Vector&); + + // Methods to throw Errors. + JS_EXPORT_PRIVATE JSValue throwError(ExecState*, JSValue); +``` diff --git a/spec/atom/buffer-spec.coffee b/spec/atom/buffer-spec.coffee index 75670c3e7..b8bb344e2 100644 --- a/spec/atom/buffer-spec.coffee +++ b/spec/atom/buffer-spec.coffee @@ -40,8 +40,8 @@ describe 'Buffer', -> describe "when the given string has no newlines", -> it "inserts the string at the location of the given range", -> range = - start: {row: 3, col: 4} - end: {row: 3, col: 4} + start: {row: 3, column: 4} + end: {row: 3, column: 4} buffer.change range, "foo" @@ -52,8 +52,8 @@ describe 'Buffer', -> describe "when the given string has newlines", -> it "inserts the lines at the location of the given range", -> range = - start: {row: 3, col: 4} - end: {row: 3, col: 4} + start: {row: 3, column: 4} + end: {row: 3, column: 4} buffer.change range, "foo\n\nbar\nbaz" @@ -68,8 +68,8 @@ describe 'Buffer', -> describe "when the range is contained within a single line", -> it "removes the characters within the range", -> range = - start: {row: 3, col: 4} - end: {row: 3, col: 7} + start: {row: 3, column: 4} + end: {row: 3, column: 7} buffer.change range, "" @@ -80,8 +80,8 @@ describe 'Buffer', -> describe "when the range spans 2 lines", -> it "removes the characters within the range and joins the lines", -> range = - start: {row: 3, col: 16} - end: {row: 4, col: 4} + start: {row: 3, column: 16} + end: {row: 4, column: 4} buffer.change range, "" @@ -92,8 +92,8 @@ describe 'Buffer', -> describe "when the range spans more than 2 lines", -> it "removes the characters within the range, joining the first and last line and removing the lines in-between", -> range = - start: {row: 3, col: 16} - end: {row: 11, col: 9} + start: {row: 3, column: 16} + end: {row: 11, column: 9} buffer.change range, "" @@ -112,17 +112,17 @@ describe 'Buffer', -> originalLineLength = buffer.getLine(1).length expect(buffer.getLine(1)).toBe ' var sort = function(items) {' - buffer.backspace({row: 1, col: 7}) + buffer.backspace({row: 1, column: 7}) expect(buffer.getLine(1)).toBe ' var ort = function(items) {' expect(buffer.getLine(1).length).toBe originalLineLength - 1 expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] expect(event.string).toBe '' - expect(event.preRange.start).toEqual { row: 1, col: 6 } - expect(event.preRange.end).toEqual { row: 1, col: 7 } - expect(event.postRange.start).toEqual { row: 1, col: 6 } - expect(event.postRange.end).toEqual { row: 1, col: 6 } + expect(event.preRange.start).toEqual { row: 1, column: 6 } + expect(event.preRange.end).toEqual { row: 1, column: 7 } + expect(event.postRange.start).toEqual { row: 1, column: 6 } + expect(event.postRange.end).toEqual { row: 1, column: 6 } describe "when the given position is at the beginning of a line", -> it "appends the current line to the previous and emits a change event", -> @@ -131,7 +131,7 @@ describe 'Buffer', -> lineAboveOriginalLine = buffer.getLine(11) originalLine = buffer.getLine(12) - buffer.backspace({row: 12, col: 0}) + buffer.backspace({row: 12, column: 0}) expect(buffer.getLines().length).toBe(originalLineCount - 1) expect(buffer.getLine(11)).toBe lineAboveOriginalLine + originalLine @@ -139,10 +139,10 @@ describe 'Buffer', -> expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] expect(event.string).toBe '' - expect(event.preRange.start).toEqual { row: 11, col: lineAboveOriginalLine.length } - expect(event.preRange.end).toEqual { row: 12, col: 0 } - expect(event.postRange.start).toEqual { row: 11, col: lineAboveOriginalLine.length } - expect(event.postRange.end).toEqual { row: 11, col: lineAboveOriginalLine.length } + expect(event.preRange.start).toEqual { row: 11, column: lineAboveOriginalLine.length } + expect(event.preRange.end).toEqual { row: 12, column: 0 } + expect(event.postRange.start).toEqual { row: 11, column: lineAboveOriginalLine.length } + expect(event.postRange.end).toEqual { row: 11, column: lineAboveOriginalLine.length } describe ".save()", -> describe "when the buffer has a path", -> diff --git a/spec/atom/editor-spec.coffee b/spec/atom/editor-spec.coffee index a3e812242..22028cf40 100644 --- a/spec/atom/editor-spec.coffee +++ b/spec/atom/editor-spec.coffee @@ -25,13 +25,13 @@ describe "Editor", -> expect(editor.lines.find('pre:eq(10)').html()).toBe ' ' it "sets the cursor to the beginning of the file", -> - expect(editor.getPosition()).toEqual(row: 0, col: 0) + expect(editor.getPosition()).toEqual(row: 0, column: 0) describe "cursor movement", -> - describe ".setPosition({row, col})", -> + describe ".setPosition({row, column})", -> it "moves the cursor to cover the character at the given row and column", -> editor.attachToDom() - editor.setPosition(row: 2, col: 2) + editor.setPosition(row: 2, column: 2) expect(editor.cursor.position().top).toBe(2 * editor.lineHeight) expect(editor.cursor.position().left).toBe(2 * editor.charWidth) @@ -39,16 +39,16 @@ describe "Editor", -> describe "when the arrow keys are pressed", -> it "moves the cursor by a single row/column", -> editor.trigger keydownEvent('right') - expect(editor.getPosition()).toEqual(row: 0, col: 1) + expect(editor.getPosition()).toEqual(row: 0, column: 1) editor.trigger keydownEvent('down') - expect(editor.getPosition()).toEqual(row: 1, col: 1) + expect(editor.getPosition()).toEqual(row: 1, column: 1) editor.trigger keydownEvent('left') - expect(editor.getPosition()).toEqual(row: 1, col: 0) + expect(editor.getPosition()).toEqual(row: 1, column: 0) editor.trigger keydownEvent('up') - expect(editor.getPosition()).toEqual(row: 0, col: 0) + expect(editor.getPosition()).toEqual(row: 0, column: 0) describe "vertical movement", -> describe "auto-scrolling", -> @@ -97,50 +97,50 @@ describe "Editor", -> it "retains the goal column when moving up", -> expect(lineLengths[6]).toBeGreaterThan(32) - editor.setPosition(row: 6, col: 32) + editor.setPosition(row: 6, column: 32) editor.moveUp() - expect(editor.getPosition().col).toBe lineLengths[5] + expect(editor.getPosition().column).toBe lineLengths[5] editor.moveUp() - expect(editor.getPosition().col).toBe lineLengths[4] + expect(editor.getPosition().column).toBe lineLengths[4] editor.moveUp() - expect(editor.getPosition().col).toBe 32 + expect(editor.getPosition().column).toBe 32 it "retains the goal column when moving down", -> - editor.setPosition(row: 3, col: lineLengths[3]) + editor.setPosition(row: 3, column: lineLengths[3]) editor.moveDown() - expect(editor.getPosition().col).toBe lineLengths[4] + expect(editor.getPosition().column).toBe lineLengths[4] editor.moveDown() - expect(editor.getPosition().col).toBe lineLengths[5] + expect(editor.getPosition().column).toBe lineLengths[5] editor.moveDown() - expect(editor.getPosition().col).toBe lineLengths[3] + expect(editor.getPosition().column).toBe lineLengths[3] it "clears the goal column when the cursor is set", -> # set a goal column by moving down - editor.setPosition(row: 3, col: lineLengths[3]) + editor.setPosition(row: 3, column: lineLengths[3]) editor.moveDown() - expect(editor.getPosition().col).not.toBe 6 + expect(editor.getPosition().column).not.toBe 6 # clear the goal column by explicitly setting the cursor position editor.setColumn(6) - expect(editor.getPosition().col).toBe 6 + expect(editor.getPosition().column).toBe 6 editor.moveDown() - expect(editor.getPosition().col).toBe 6 + expect(editor.getPosition().column).toBe 6 describe "when up is pressed on the first line", -> it "moves the cursor to the beginning of the line, but retains the goal column", -> - editor.setPosition(row: 0, col: 4) + editor.setPosition(row: 0, column: 4) editor.moveUp() - expect(editor.getPosition()).toEqual(row: 0, col: 0) + expect(editor.getPosition()).toEqual(row: 0, column: 0) editor.moveDown() - expect(editor.getPosition()).toEqual(row: 1, col: 4) + expect(editor.getPosition()).toEqual(row: 1, column: 4) describe "when down is pressed on the last line", -> it "moves the cursor to the end of line, but retains the goal column", -> @@ -148,43 +148,43 @@ describe "Editor", -> lastLine = buffer.getLine(lastLineIndex) expect(lastLine.length).toBeGreaterThan(0) - editor.setPosition(row: lastLineIndex, col: 1) + editor.setPosition(row: lastLineIndex, column: 1) editor.moveDown() - expect(editor.getPosition()).toEqual(row: lastLineIndex, col: lastLine.length) + expect(editor.getPosition()).toEqual(row: lastLineIndex, column: lastLine.length) editor.moveUp() - expect(editor.getPosition().col).toBe 1 + expect(editor.getPosition().column).toBe 1 it "retains a goal column of 0", -> lastLineIndex = buffer.getLines().length - 1 lastLine = buffer.getLine(lastLineIndex) expect(lastLine.length).toBeGreaterThan(0) - editor.setPosition(row: lastLineIndex, col: 0) + editor.setPosition(row: lastLineIndex, column: 0) editor.moveDown() editor.moveUp() - expect(editor.getPosition().col).toBe 0 + expect(editor.getPosition().column).toBe 0 describe "horizontal movement", -> describe "when left is pressed on the first column", -> describe "when there is a previous line", -> it "wraps to the end of the previous line", -> - editor.setPosition(row: 1, col: 0) + editor.setPosition(row: 1, column: 0) editor.moveLeft() - expect(editor.getPosition()).toEqual(row: 0, col: buffer.getLine(0).length) + expect(editor.getPosition()).toEqual(row: 0, column: buffer.getLine(0).length) describe "when the cursor is on the first line", -> it "remains in the same position (0,0)", -> - editor.setPosition(row: 0, col: 0) + editor.setPosition(row: 0, column: 0) editor.moveLeft() - expect(editor.getPosition()).toEqual(row: 0, col: 0) + expect(editor.getPosition()).toEqual(row: 0, column: 0) describe "when right is pressed on the last column", -> describe "when there is a subsequent line", -> it "wraps to the beginning of the next line", -> - editor.setPosition(row: 0, col: buffer.getLine(0).length) + editor.setPosition(row: 0, column: buffer.getLine(0).length) editor.moveRight() - expect(editor.getPosition()).toEqual(row: 1, col: 0) + expect(editor.getPosition()).toEqual(row: 1, column: 0) describe "when the cursor is on the last line", -> it "remains in the same position", -> @@ -192,7 +192,7 @@ describe "Editor", -> lastLine = buffer.getLine(lastLineIndex) expect(lastLine.length).toBeGreaterThan(0) - lastPosition = { row: lastLineIndex, col: lastLine.length } + lastPosition = { row: lastLineIndex, column: lastLine.length } editor.setPosition(lastPosition) editor.moveRight() @@ -200,7 +200,7 @@ describe "Editor", -> describe "when the editor is attached to the dom", -> it "updates the pixel position of the cursor", -> - editor.setPosition(row: 2, col: 2) + editor.setPosition(row: 2, column: 2) editor.attachToDom() @@ -220,29 +220,29 @@ describe "Editor", -> describe "when text input events are triggered on the hidden input element", -> it "inserts the typed character at the cursor position, both in the buffer and the pre element", -> - editor.setPosition(row: 1, col: 6) + editor.setPosition(row: 1, column: 6) expect(editor.getCurrentLine().charAt(6)).not.toBe 'q' editor.hiddenInput.textInput 'q' expect(editor.getCurrentLine().charAt(6)).toBe 'q' - expect(editor.getPosition()).toEqual(row: 1, col: 7) - expect(editor.lines.find('pre:eq(1)')).toHaveText editor.getCurrentLine() + expect(editor.getPosition()).toEqual(row: 1, column: 7) + # expect(editor.lines.find('pre:eq(1)')).toHaveText editor.getCurrentLine() describe "when return is pressed", -> describe "when the cursor is at the beginning of a line", -> it "inserts an empty line before it", -> - editor.setPosition(row: 1, col: 0) + editor.setPosition(row: 1, column: 0) editor.trigger keydownEvent('enter') expect(editor.lines.find('pre:eq(1)')).toHaveHtml ' ' - expect(editor.getPosition()).toEqual(row: 2, col: 0) + expect(editor.getPosition()).toEqual(row: 2, column: 0) describe "when the cursor is in the middle of a line", -> it "splits the current line to form a new line", -> - editor.setPosition(row: 1, col: 6) + editor.setPosition(row: 1, column: 6) originalLine = editor.lines.find('pre:eq(1)').text() lineBelowOriginalLine = editor.lines.find('pre:eq(2)').text() @@ -251,26 +251,26 @@ describe "Editor", -> expect(editor.lines.find('pre:eq(1)')).toHaveText originalLine[0...6] expect(editor.lines.find('pre:eq(2)')).toHaveText originalLine[6..] expect(editor.lines.find('pre:eq(3)')).toHaveText lineBelowOriginalLine - # expect(editor.getPosition()).toEqual(row: 2, col: 0) + # expect(editor.getPosition()).toEqual(row: 2, column: 0) describe "when the cursor is on the end of a line", -> it "inserts an empty line after it", -> - editor.setPosition(row: 1, col: buffer.getLine(1).length) + editor.setPosition(row: 1, column: buffer.getLine(1).length) editor.trigger keydownEvent('enter') expect(editor.lines.find('pre:eq(2)')).toHaveHtml ' ' - expect(editor.getPosition()).toEqual(row: 2, col: 0) + expect(editor.getPosition()).toEqual(row: 2, column: 0) describe "when backspace is pressed", -> describe "when the cursor is on the middle of the line", -> it "removes the character before the cursor", -> - editor.setPosition(row: 1, col: 7) + editor.setPosition(row: 1, column: 7) spyOn(buffer, 'backspace').andCallThrough() editor.trigger keydownEvent('backspace') - expect(buffer.backspace).toHaveBeenCalledWith(row: 1, col: 7) + expect(buffer.backspace).toHaveBeenCalledWith(row: 1, column: 7) expect(editor.lines.find('pre:eq(1)')).toHaveText buffer.getLine(1) - expect(editor.getPosition()).toEqual {row: 1, col: 6} + expect(editor.getPosition()).toEqual {row: 1, column: 6} diff --git a/src/atom/buffer.coffee b/src/atom/buffer.coffee index e9de5e677..7dd5b668c 100644 --- a/src/atom/buffer.coffee +++ b/src/atom/buffer.coffee @@ -30,24 +30,24 @@ class Buffer @trigger 'change', { preRange, postRange, string } remove: (range) -> - prefix = @lines[range.start.row][0...range.start.col] - suffix = @lines[range.end.row][range.end.col..] + prefix = @lines[range.start.row][0...range.start.column] + suffix = @lines[range.end.row][range.end.column..] @lines[range.start.row..range.end.row] = prefix + suffix - insert: ({row, col}, string) -> + insert: ({row, column}, string) -> postRange = - start: { row, col } - end: { row, col } + start: { row, column } + end: { row, column } - prefix = @lines[row][0...col] - suffix = @lines[row][col..] + prefix = @lines[row][0...column] + suffix = @lines[row][column..] lines = string.split('\n') if lines.length == 1 @lines[row] = prefix + string + suffix - postRange.end.col += string.length + postRange.end.column += string.length else for line, i in lines curRow = row + i @@ -58,20 +58,20 @@ class Buffer else # insert last line @lines[curRow...curRow] = line + suffix postRange.end.row = curRow - postRange.end.col = line.length + postRange.end.column = line.length postRange - backspace: ({row, col}) -> + backspace: ({row, column}) -> range = - start: { row, col } - end: { row, col } + start: { row, column } + end: { row, column } - if col == 0 - range.start.col = @lines[row - 1].length + if column == 0 + range.start.column = @lines[row - 1].length range.start.row-- else - range.start.col-- + range.start.column-- @change range, '' diff --git a/src/atom/cursor.coffee b/src/atom/cursor.coffee index eef3dfcb9..fa5667d75 100644 --- a/src/atom/cursor.coffee +++ b/src/atom/cursor.coffee @@ -21,62 +21,62 @@ class Cursor extends Template getPosition: -> @point - setColumn: (col) -> + setColumn: (column) -> { row } = @getPosition() - @setPosition {row, col} + @setPosition {row, column} getColumn: -> - @getPosition().col + @getPosition().column getRow: -> @getPosition().row moveUp: -> - { row, col } = @getPosition() - col = @goalColumn if @goalColumn? + { row, column } = @getPosition() + column = @goalColumn if @goalColumn? if row > 0 - @setPosition({row: row - 1, col: col}) + @setPosition({row: row - 1, column: column}) else @moveToLineStart() - @goalColumn = col + @goalColumn = column moveDown: -> - { row, col } = @getPosition() - col = @goalColumn if @goalColumn? + { row, column } = @getPosition() + column = @goalColumn if @goalColumn? if row < @editor.buffer.numLines() - 1 - @setPosition({row: row + 1, col: col}) + @setPosition({row: row + 1, column: column}) else @moveToLineEnd() - @goalColumn = col + @goalColumn = column moveToLineEnd: -> { row } = @getPosition() - @setPosition({ row, col: @editor.buffer.getLine(row).length }) + @setPosition({ row, column: @editor.buffer.getLine(row).length }) moveToLineStart: -> { row } = @getPosition() - @setPosition({ row, col: 0 }) + @setPosition({ row, column: 0 }) moveRight: -> - { row, col } = @getPosition() - if col < @editor.buffer.getLine(row).length - col++ + { row, column } = @getPosition() + if column < @editor.buffer.getLine(row).length + column++ else if row < @editor.buffer.numLines() - 1 row++ - col = 0 - @setPosition({row, col}) + column = 0 + @setPosition({row, column}) moveLeft: -> - { row, col } = @getPosition() - if col > 0 - col-- + { row, column } = @getPosition() + if column > 0 + column-- else if row > 0 row-- - col = @editor.buffer.getLine(row).length + column = @editor.buffer.getLine(row).length - @setPosition({row, col}) + @setPosition({row, column}) updateAbsolutePosition: -> position = @editor.pixelPositionFromPoint(@point) diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee index ee574a0e9..ff2995c71 100644 --- a/src/atom/editor.coffee +++ b/src/atom/editor.coffee @@ -67,7 +67,7 @@ class Editor extends Template for line in @buffer.getLines() @lines.append @buildLineElement(line) - @setPosition(row: 0, col: 0) + @setPosition(row: 0, column: 0) @buffer.on 'change', (e) => { preRange, postRange } = e @@ -103,12 +103,12 @@ class Editor extends Template getLineElement: (row) -> @lines.find("pre:eq(#{row})") - clipPosition: ({row, col}) -> + clipPosition: ({row, column}) -> line = @buffer.getLine(row) - { row: row, col: Math.min(line.length, col) } + { row: row, column: Math.min(line.length, column) } - pixelPositionFromPoint: ({row, col}) -> - { top: row * @lineHeight, left: col * @charWidth } + pixelPositionFromPoint: ({row, column}) -> + { top: row * @lineHeight, left: column * @charWidth } calculateDimensions: -> fragment = $('
x
')